Options
When using ReactForm, you can pass options to customize its behavior. These options allow you to define global behavior through ReactFormProvider or form-specific behavior through ReactForm props.
componentPrefix
- Type:
string - Default:
''
Adds a prefix to component names. Not used in React (kept for consistency with React version).
isNewItemCheck
- Type:
Function - Arguments:
(context) => Boolean
errorAdapter
- Type:
Function - Arguments:
(error) => Object
isArchivedItemCheck
- Type:
Function - Arguments:
(response) => Boolean
Function that determines if an item is archived. Called after read, create, update, archive, or unarchive operations.
{
isArchivedItemCheck: (response) => {
return response.isArchived === true;
};
}schemaToFields
- Type:
Function - Arguments:
(schema) => Array<{ name: string, label?: string, value?: any }>
Custom function to extract field definitions from your validation schema. This function is called when a schema prop is provided to <ReactForm>.
With Zod
import { z } from "zod";
const schema = z.object({
name: z
.string()
.min(2, "Name must be at least 2 characters")
.default("") // Must Pass default for validate schema properly
.describe("Name"), // Customize Label
email: z
.string()
.email("Invalid email address")
.default("") // Must Pass default for validate schema properly
.describe("Email"), // Customize Label
});
const schemaToFields = (schema) => {
return Object.keys(schema.shape).map((key) => {
const zodField = schema.shape[key];
/*
- "defaultValue" & "description" key can be vary or not found
with different zod version (current - v4)
- check it with console.log("zodField:", zodField);
- Need to map the keys accordingly
*/
const defaultValue = zodField.def.defaultValue;
const label = zodField.description;
return {
name: key,
label: label || key.charAt(0).toUpperCase() + key.slice(1),
value: defaultValue,
};
});
};
<ReactFormProvider schemaToFields={schemaToFields}>
<ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;With Yup
import * as yup from "yup";
const schema = yup.object({
name: yup
.string()
.min(2, "Name must be at least 2 characters")
.default("") // Must Pass default for validate schema properly
.label("Name"), // Customize Label
email: yup
.string()
.email("Invalid email address")
.default("") // Must Pass default for validate schema properly
.label("Email"), // Customize Label
});
const schemaToFields = (schema) => {
return Object.keys(schema.fields).map((key) => {
const yupField = schema.fields[key];
/*
- ".spec.default" & ".spec.label" key can be vary or not found
with different yup version (current - 1.7.1)
- check it with console.log("yupField:", yupField);
- Need to map the keys accordingly
*/
const label = yupField.spec.label;
const defaultValue = yupField.spec.default;
return {
name: key,
label: label || key.charAt(0).toUpperCase() + key.slice(1),
value: defaultValue,
};
});
};
<ReactFormProvider schemaToFields={schemaToFields}>
<ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;With Custom Validation Library
// Example with a custom validation library
const schemaToFields = (schema) => {
return Object.keys(schema.properties).map((key) => {
const prop = schema.properties[key];
return {
name: key,
label: prop.label || key.charAt(0).toUpperCase() + key.slice(1),
value: prop.default || null,
};
});
};validateSchema
- Type:
Function - Arguments:
(schema, values) => Promise<{ success: boolean, errors: Object }>
Custom validation function that accepts your schema and form values, and returns a validation result. This function is called automatically when create or update is triggered.
Return Format:
{
success: boolean, // true if validation passed, false if failed
errors: { // Object with field names as keys
fieldName: {
message: string, // Required: error message
[key: string]: any // Optional: additional error properties
}
}
}With Zod
import { z } from "zod";
const schema = z.object({
name: z
.string()
.min(2, "Name must be at least 2 characters")
.default("") // Must Pass default for validate schema properly
.describe("Name"), // Customize Label
email: z
.string()
.email("Invalid email address")
.default("") // Must Pass default for validate schema properly
.describe("Email"), // Customize Label
});
const validateSchema = async (schema, values) => {
const result = schema.safeParse(values);
if (result.success) {
return { success: true, errors: {} };
}
const fieldErrors = result.error.issues.reduce((acc, issue) => {
const field = issue.path[0];
if (!field) return acc;
acc[field] = { message: issue.message };
return acc;
}, {});
return { success: false, errors: fieldErrors };
};
<ReactFormProvider validateSchema={validateSchema}>
<ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;With Yup
import * as yup from "yup";
const schema = yup.object({
name: yup
.string()
.min(2, "Name must be at least 2 characters")
.default("") // Must Pass default for validate schema properly
.label("Name"), // Customize Label
email: yup
.string()
.email("Invalid email address")
.default("") // Must Pass default for validate schema properly
.label("Email"), // Customize Label
});
const validateSchema = async (schema, values) => {
try {
await schema.validate(values, { abortEarly: false });
return { success: true, errors: {} };
} catch (error) {
const fieldErrors = {};
if (error.inner) {
error.inner.forEach((err) => {
if (err.path) {
fieldErrors[err.path] = { message: err.message };
}
});
}
return { success: false, errors: fieldErrors };
}
};
<ReactFormProvider validateSchema={validateSchema}>
<ReactForm schema={schema} {...otherProps} />
</ReactFormProvider>;With Custom Validation Library
// Example with a custom validation library
const validateSchema = async (schema, values) => {
try {
// Your validation logic here
const result = await myCustomValidator.validate(schema, values);
if (result.valid) {
return { success: true, errors: {} };
}
// Convert your library's errors to ReactForm format
const fieldErrors = {};
result.errors.forEach((error) => {
fieldErrors[error.field] = {
message: error.message,
// Include any additional error properties
...error.metadata,
};
});
return { success: false, errors: fieldErrors };
} catch (error) {
return {
success: false,
errors: {
_form: { message: "Validation failed" },
},
};
}
};Advanced: Cross-Field Validation (Zod)
import { z } from "zod";
const schema = z
.object({
password: z
.string()
.min(8, "Password must be at least 8 characters")
.regex(/[A-Z]/, "Must contain uppercase letter")
.regex(/[a-z]/, "Must contain lowercase letter")
.regex(/[0-9]/, "Must contain a number"),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"], // Specify which field the error belongs to
});
const validateSchema = async (schema, values) => {
const result = schema.safeParse(values);
if (result.success) {
return { success: true, errors: {} };
}
const fieldErrors = result.error.issues.reduce((acc, issue) => {
// Handle cross-field validation errors
const field = issue.path.length > 0 ? issue.path[0] : null;
if (!field) return acc;
acc[field] = {
message: issue.message,
};
return acc;
}, {});
return { success: false, errors: fieldErrors };
};Advanced: Cross-Field Validation (Yup)
import * as yup from "yup";
const schema = yup.object({
password: yup
.string()
.min(8, "Password must be at least 8 characters")
.matches(/[A-Z]/, "Must contain uppercase letter")
.matches(/[a-z]/, "Must contain lowercase letter")
.matches(/[0-9]/, "Must contain a number")
.required(),
confirmPassword: yup
.string()
.oneOf([yup.ref("password")], "Passwords don't match")
.required(),
});
const validateSchema = async (schema, values) => {
try {
await schema.validate(values, { abortEarly: false });
return { success: true, errors: {} };
} catch (error) {
const fieldErrors = {};
if (error.inner) {
error.inner.forEach((err) => {
if (err.path) {
fieldErrors[err.path] = { message: err.message };
}
});
}
return { success: false, errors: fieldErrors };
}
};Complete Example
Here's a complete example using Zod with both schemaToFields and validateSchema:
import {
ReactFormProvider,
ReactForm,
ReactFormField,
} from "@7span/react-form";
import { z } from "zod";
// Define schema
const schema = z.object({
name: z
.string()
.min(2, "Name must be at least 2 characters")
.default("") // Must need to pass for default value
.describe("Name"), // For customizing name
email: z
.string()
.email("Invalid email address")
.default("")
.describe("Email"),
age: z.number().min(18, "Must be 18 or older").default(0).describe("Age"),
});
// Schema to fields converter
const schemaToFields = (schema) => {
return Object.keys(schema.shape).map((key) => {
const zodField = schema.shape[key];
/*
- "defaultValue" & "description" key can be vary or not found
with different zod version (current - v4)
- check it with console.log("zodField:", zodField);
- Need to map the keys accordingly
*/
const defaultValue = zodField.def.defaultValue;
const label = zodField.description;
return {
name: key,
label: label || key.charAt(0).toUpperCase() + key.slice(1),
value: defaultValue,
};
});
};
// Validation function
const validateSchema = async (schema, values) => {
const result = schema.safeParse(values);
if (result.success) {
return { success: true, errors: {} };
}
const fieldErrors = result.error.issues.reduce((acc, issue) => {
const field = issue.path[0];
if (!field) return acc;
acc[field] = { message: issue.message };
return acc;
}, {});
return { success: false, errors: fieldErrors };
};
function App() {
return (
<ReactFormProvider
schemaToFields={schemaToFields}
validateSchema={validateSchema}
>
<ReactForm schema={schema} create={create} update={update}>
{({ context }) => (
<>
<ReactFormField name="name">
{({ field, label, error }) => (
<>
<label>{label}</label>
<input {...field} />
{error && <span>{error.message}</span>}
</>
)}
</ReactFormField>
{/* More fields... */}
</>
)}
</ReactForm>
</ReactFormProvider>
);
}