<ReactForm> - The Root Component
The root component that wires everything together. It manages the entire data lifecycle - state, fields, values, and API calls - and provides context for all child components.
It's the only component you must include. Every other component depends on the context provided by <ReactForm>.
<ReactForm>
{({ context }) => (
// Child Components
)}
</ReactForm>Props
itemId
- Type:
Number|String
The identifier passed to isNewItemCheck for mode detection. Use special strings like '+' or 'create' to indicate create mode, or pass the database ID for update mode.
// Create mode
<ReactForm itemId="create" />
// Update mode
<ReactForm itemId={7} />
<ReactForm itemId="abc-123" />
// From route params
<ReactForm itemId={params.id} />See how to use itemId in isNewItemCheck here
create
- Type:
Function - Signature:
(context) => Promise<ReactFormApiResponse>
Async function called when the create button is clicked. Receives the full context object and must return a promise resolving to a ReactFormApiResponse.
function create(context) {
return fetch("/api/users/").then((res) => {
return {
values: { ... }, // {key:value} pairs for each field
};
});
}Important
Do not catch errors in your create, read, update, delete, archive, or unarchive methods. Read more
read
- Type:
Function - Signature:
(itemId, context) => Promise<ReactFormApiResponse>
Async function called when the component is mounted and update mode is detected. Must return a promise resolving to a ReactFormApiResponse.
function read(itemId, context) {
return fetch(`/api/users/${itemId}`).then((res) => {
return {
values: { ... }, // {key:value} pairs for each field
};
});
}update
- Type:
Function - Signature:
(itemId, context) => Promise<ReactFormApiResponse>
Async function called when the update button is clicked. Must return a promise resolving to a ReactFormApiResponse.
function update(itemId, context) {
return fetch(`/api/users/${itemId}`, {
method: "PUT",
body: JSON.stringify(context.values),
}).then((res) => {
return {
values: { ... }, // {key:value} pairs for each field
};
});
}delete
- Type:
Function - Signature:
(itemId, context) => Promise<ReactFormApiResponse>
Async function called when the delete button is clicked. Must return a promise resolving to a ReactFormApiResponse.
function deleteItem(itemId, context) {
return fetch(`/api/users/${itemId}`, {
method: "DELETE",
}).then((res) => {
return {};
});
}archive
- Type:
Function - Signature:
(itemId, context) => Promise<ReactFormApiResponse>
Async function called when the archive button is clicked. Must return a promise resolving to a ReactFormApiResponse.
function archive(itemId, context) {
return fetch(`/api/users/${itemId}/archive`, {
method: "POST",
}).then((res) => {
return {
values: { ... },
isArchived: true,
};
});
}unarchive
- Type:
Function - Signature:
(itemId, context) => Promise<ReactFormApiResponse>
Async function called when the unarchive button is clicked. Must return a promise resolving to a ReactFormApiResponse.
function unarchive(itemId, context) {
return fetch(`/api/users/${itemId}/unarchive`, {
method: "POST",
}).then((res) => {
return {
values: { ... },
isArchived: false,
};
});
}isNewItemCheck
- Type:
Function - Optional
Custom function to determine if item is new (overrides provider). See Configurations for more details.
isArchivedItemCheck
- Type:
Function - Optional
Custom function to check if item is archived (overrides provider). See Configurations for more details.
errorAdapter
- Type:
Function - Optional
Custom error adapter function (overrides provider). See Configurations for more details.
componentPrefix
- Type:
String - Optional
Component prefix (overrides provider). Not used in React (kept for consistency with React version).
className
- Type:
String - Optional
- Default:
"react-form"
CSS class name for the form container.
schema
- Type:
Any - Optional
Validation schema (Zod, Yup, or custom) - passed to validateSchema function. When provided, the form will automatically validate before submitting.
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
});
<ReactForm schema={schema} {...otherProps} />;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
});
<ReactForm schema={schema} {...otherProps} />;schemaToFields
- Type:
Function - Optional
Custom function to extract fields from schema (overrides provider). This function is called when a schema prop is provided.
With Zod
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,
};
});
};
<ReactForm schema={schema} schemaToFields={schemaToFields} {...otherProps} />;With Yup
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,
};
});
};
<ReactForm schema={schema} schemaToFields={schemaToFields} {...otherProps} />;validateSchema
- Type:
Function - Optional
Custom validation function (overrides provider). This function is called automatically when create or update is triggered.
Signature: (schema, values) => Promise<{ success: boolean, errors: Object }>
With Zod
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 };
};
<ReactForm schema={schema} validateSchema={validateSchema} {...otherProps} />;With Yup
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 };
}
};
<ReactForm schema={schema} validateSchema={validateSchema} {...otherProps} />;Complete Example For Local Configuration
With Zod
import { ReactForm, ReactFormField } from "@7span/react-form";
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
});
// Local schemaToFields (overrides provider)
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,
};
});
};
// Local validateSchema (overrides provider)
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 MyForm() {
return (
<ReactForm
schema={schema}
schemaToFields={schemaToFields}
validateSchema={validateSchema}
create={create}
update={update}
>
{({ context }) => (
<>
<ReactFormField name="name">
{({ field, label, error }) => (
<>
<label>{label}</label>
<input {...field} />
{error && <span>{error.message}</span>}
</>
)}
</ReactFormField>
<ReactFormField name="email">
{({ field, label, error }) => (
<>
<label>{label}</label>
<input type="email" {...field} />
{error && <span>{error.message}</span>}
</>
)}
</ReactFormField>
</>
)}
</ReactForm>
);
}With Yup
import { ReactForm, ReactFormField } from "@7span/react-form";
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
});
// Local schemaToFields (overrides provider)
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,
};
});
};
// Local validateSchema (overrides provider)
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 };
}
};
function MyForm() {
return (
<ReactForm
schema={schema}
schemaToFields={schemaToFields}
validateSchema={validateSchema}
create={create}
update={update}
>
{({ context }) => (
<>
<ReactFormField name="name">
{({ field, label, error }) => (
<>
<label>{label}</label>
<input {...field} />
{error && <span>{error.message}</span>}
</>
)}
</ReactFormField>
<ReactFormField name="email">
{({ field, label, error }) => (
<>
<label>{label}</label>
<input type="email" {...field} />
{error && <span>{error.message}</span>}
</>
)}
</ReactFormField>
</>
)}
</ReactForm>
);
}Render Props
context
The only render prop available in <ReactForm> is context. It exposes a set of variables that you can access.
Context Properties
| Key | Description |
|---|---|
| context | Object The context object |
| readItem | Function Wrapper of read function. You can use this method to manually do the read call |
| updateItem | Function Wrapper of update function. You can use this instead of <ReactFormUpdate> |
| deleteItem | Function Wrapper of delete function. You can use this instead of <ReactFormDelete> |
| archiveItem | Function Wrapper of archive function. You can use this instead of <ReactFormArchive> |
| unarchiveItem | Function Wrapper of unarchive function. You can use this instead of <ReactFormUnarchive> |
| setValue | Function Manually set a field value |
| values | Object Current form values |
| dirtyValues | Object Only fields that have been modified |
| errors | Object Field-level errors |
| error | Object Form-level error |
| isLoading | Boolean True when any async operation is in progress |
| isNewItem | Boolean True when in create mode |
| isArchived | Boolean True if item is archived |
| isReading | Boolean True while reading |
| isCreating | Boolean True while creating |
| isUpdating | Boolean True while updating |
| isDeleting | Boolean True while deleting |
| isArchiving | Boolean True while archiving |
| isUnarchiving | Boolean True while unarchiving |
See the Context Object documentation for a complete reference.