### Install Conform with Valibot
Source: https://github.com/edmundhung/conform/blob/main/docs/installation.md
Install Conform, the React helper, and the Valibot validation library using npm.
```bash
npm install @conform-to/react @conform-to/valibot valibot
```
--------------------------------
### Install Conform with Yup
Source: https://github.com/edmundhung/conform/blob/main/docs/installation.md
Install Conform, the React helper, and the Yup validation library using npm.
```bash
npm install @conform-to/react @conform-to/yup yup
```
--------------------------------
### Install Conform with Zod
Source: https://github.com/edmundhung/conform/blob/main/docs/installation.md
Install Conform, the React helper, and the Zod validation library using npm.
```bash
npm install @conform-to/react @conform-to/zod zod
```
--------------------------------
### Install Conform with Zod, Valibot, or Yup
Source: https://context7.com/edmundhung/conform/llms.txt
Install the core `@conform-to/react` package along with the appropriate schema adapter for Zod, Valibot, or Yup.
```bash
# With Zod v3
npm install @conform-to/react @conform-to/zod zod
# With Zod v4
npm install @conform-to/react @conform-to/zod zod
# With Valibot
npm install @conform-to/react @conform-to/valibot valibot
# With Yup
npm install @conform-to/react @conform-to/yup yup
```
--------------------------------
### Install Conform and Zod Integration
Source: https://github.com/edmundhung/conform/blob/main/docs/tutorial.md
Install the necessary Conform packages for React and Zod integration using npm.
```sh
npm install @conform-to/react @conform-to/zod --save
```
--------------------------------
### Example Usage
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getInputProps.md
Demonstrates how to use getInputProps with a password input field.
```APIDOC
## Example
```tsx
import { useForm, getInputProps } from '@conform-to/react';
function Example() {
const [form, fields] = useForm();
return ;
}
```
```
--------------------------------
### Get Collection Props
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getCollectionProps.md
A basic example demonstrating how to use `getCollectionProps` to generate props for a collection of radio buttons.
```tsx
import { useForm, getCollectionProps } from '@conform-to/react';
function Example() {
const [form, fields] = useForm();
return (
<>
{getCollectionProps(fields.color, {
type: 'radio',
options: ['red', 'green', 'blue'],
}).map((props) => (
))}
>
);
}
```
--------------------------------
### Basic Usage Example
Source: https://github.com/edmundhung/conform/blob/main/docs/api/zod/parseWithZod.md
An example demonstrating how to use parseWithZod within a React component's form validation.
```APIDOC
## Example
```tsx
import { useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
// If you are using Zod v4, update the imports:
// import { parseWithZod } from '@conform-to/zod/v4';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
password: z.string(),
});
function Example() {
const [form, fields] = useForm({
onValidate({ formData }) {
return parseWithZod(formData, { schema });
},
});
// ...
}
```
```
--------------------------------
### Example Usage
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/useInputControl.md
Demonstrates how to use the `useInputControl` hook with a custom select component to manage input value and events.
```APIDOC
## Example
```tsx
import { useForm, useInputControl } from '@conform-to/react';
import { Select, Option } from './custom-ui';
function Example() {
const [form, fields] = useForm();
const color = useInputControl(fields.color);
return (
);
}
```
```
--------------------------------
### Complete Form Example with Zod Validation and Submission
Source: https://github.com/edmundhung/conform/blob/main/docs/tutorial.md
A full example demonstrating Conform integration with Zod for schema definition, validation, and form submission handling in a Remix application. It includes input props generation and error display.
```tsx
import {
useForm,
getFormProps,
getInputProps,
getTextareaProps,
} from '@conform-to/react';
import { parseWithZod, getZodConstraint } from '@conform-to/zod';
import { type ActionFunctionArgs } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { z } from 'zod';
import { sendMessage } from '~/message';
const schema = z.object({
email:
z.string({ required_error: 'Email is required' })
.email('Email is invalid'),
message:
z.string({ required_error: 'Message is required' })
.min(10, 'Message is too short')
.max(100, 'Message is too long'),
});
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const submission = parseWithZod(formData, { schema });
if (submission.status !== 'success') {
return submission.reply();
}
const message = await sendMessage(submission.value);
if (!message.sent) {
return submission.reply({
formErrors: ['Failed to send the message. Please try again later.'],
});
}
return redirect('/messages');
}
export default function ContactUs() {
const lastResult = useActionData();
const [form, fields] = useForm({
lastResult,
constraint: getZodConstraint(schema),
shouldValidate: 'onBlur',
shouldRevalidate: 'onInput',
onValidate({ formData }) {
return parseWithZod(formData, { schema });
},
});
return (
);
}
```
--------------------------------
### Remix Login Form Example
Source: https://github.com/edmundhung/conform/blob/main/docs/integration/remix.md
Demonstrates a complete login form integration with Remix, including server-side validation using Zod and client-side validation with Conform.
```tsx
import { getFormProps, getInputProps, useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import type { ActionArgs } from '@remix-run/node';
import { json, redirect } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
password: z.string(),
remember: z.boolean().optional(),
});
export async function action({ request }: ActionArgs) {
const formData = await request.formData();
const submission = parseWithZod(formData, {
schema
});
if (submission.status !== 'success') {
return json(submission.reply());
}
// ...
}
export default function Login() {
// Last submission returned by the server
const lastResult = useActionData();
const [form, fields] = useForm({
// Sync the result of last submission
lastResult,
// Reuse the validation logic on the client
onValidate({ formData }) {
return parseWithZod(formData, {
schema
});
},
// Validate the form on blur event triggered
shouldValidate: 'onBlur',
shouldRevalidate: 'onInput',
});
return (
);
}
```
--------------------------------
### Manual Fieldset Props vs. Helper
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getFieldsetProps.md
This example demonstrates the 'before' and 'after' of using the getFieldsetProps helper. The 'after' version is more concise and readable by leveraging the helper function.
```tsx
// Before
function Example() {
return (
);
}
// After
function Example() {
return ;
}
```
--------------------------------
### Focus Delegation Example
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/useInputControl.md
Illustrates how to handle focus delegation for custom inputs when Conform fails to focus the first invalid input element.
```APIDOC
## Tips
### Focus delegation
Conform will focus on the first invalid input element if submission failed. However, this might not work if your have a custom input. To fix this, you can forward the focus from the input element by listening to the focus event and trigger `element.focus()` on the desired element.
```tsx
import { useForm, useInputControl } from '@conform-to/react';
import { Select, Option } from './custom-ui';
function Example() {
const [form, fields] = useForm();
const inputRef = useRef(null);
const color = useInputControl(fields.color);
return (
<>
inputRef.current?.focus()}
/>
<>
);
}
```
In the example above, we set up a hidden input manually instead of passing a `name` prop to the custom select component due to no control over the inner input rendered by the custom input. The input is visually hidden but still focusable thanks to the **sr-only** class from [tailwindcss](https://tailwindcss.com/docs/screen-readers#screen-reader-only-elements). When the input is focused, we delegate the focus to the custom input by calling `inputRef.current?.focus()`.
If you are not using tailwindcss, please look for a similar utility from your preferred styling solution or you can apply the following style based on the implementation of the **sr-only** class:
```tsx
const style = {
position: 'absolute',
width: '1px',
height: '1px',
padding: 0,
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
border: 0,
};
```
```
--------------------------------
### Minimize Form Boilerplate with Conform Helpers
Source: https://github.com/edmundhung/conform/blob/main/docs/tutorial.md
Use `getFormProps`, `getInputProps`, and `getTextareaProps` to reduce boilerplate when setting up native inputs. This example shows the basic structure for integrating these helpers.
```tsx
import {
useForm,
getFormProps,
getInputProps,
getTextareaProps,
} from '@conform-to/react';
import { parseWithZod, getZodConstraint } from '@conform-to/zod';
import { type ActionFunctionArgs } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { sendMessage } from '~/message';
const schema = z.object({
// ...
});
export async function action({ request }: ActionFunctionArgs) {
// ...
}
export default function ContactUs() {
const lastResult = useActionData();
const [form, fields] = useForm({
// ...
});
return (
);
}
```
--------------------------------
### Compare Manual vs. Helper Input Setup
Source: https://github.com/edmundhung/conform/blob/main/docs/accessibility.md
Illustrates the reduction in boilerplate when using `getInputProps` compared to manually setting attributes for form inputs. This helper simplifies the process of managing input properties and accessibility attributes.
```tsx
import { parseWithZod, getZodConstraint } from '@conform-to/zod';
import { useForm } from '@conform-to/react';
import { z } from 'zod';
const schema = z.object({
message:
z.string()
.min(10)
.max(100)
.regex(/^[A-Za-z0-9 ]{10-100}$/),
});
function Example() {
const [form, fields] = useForm({
constraint: getZodConstraint(schema),
onValidate({ formData }) {
return parseWithZod(formData, { schema });
},
});
return (
);
}
```
--------------------------------
### Radio Group Setup with Conform
Source: https://github.com/edmundhung/conform/blob/main/docs/checkbox-and-radio-group.md
Use this for setting up a radio group. Ensure all inputs share the same 'name' attribute. The 'initialValue' from field metadata can determine the default checked state.
```tsx
import { useForm } from '@conform-to/react';
function Example() {
const [form, fields] = useForm();
return (
);
}
```
--------------------------------
### Setup Nested Array Fields with Conform
Source: https://github.com/edmundhung/conform/blob/main/docs/complex-structures.md
Combine `getFieldset()` and `getFieldList()` to handle deeply nested structures like arrays of objects. Field names are automatically inferred for all levels.
```tsx
import { useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { z } from 'zod';
const schema = z.object({
todos: z.array(
z.object({
title: z.string(),
notes: z.string(),
}),
),
});
function Example() {
const [form, fields] = useForm({
onValidate({ formData }) {
return parseWithZod(formData, { schema });
},
});
const todos = fields.todos.getFieldList();
return (
);
}
```
--------------------------------
### Basic Usage of getYupConstraint
Source: https://github.com/edmundhung/conform/blob/main/docs/api/yup/getYupConstraint.md
Import `getYupConstraint` and pass your Yup schema to it to generate the constraint object for Conform's `useForm` hook. This example demonstrates defining a schema with string fields requiring a minimum and maximum length.
```tsx
import {
getYupConstraint,
} from '@conform-to/yup';
import {
useForm,
} from '@conform-to/react';
import * as yup from 'yup';
const schema = yup.object({
title: yup.string().required().min(5).max(20),
description: yup.string().optional().min(100).max(1000),
});
function Example() {
const [form, fields] = useForm({
constraint: getYupConstraint(schema),
});
// ...
}
```
--------------------------------
### Single Checkbox Setup with Conform
Source: https://github.com/edmundhung/conform/blob/main/docs/checkbox-and-radio-group.md
For a single checkbox, check if the 'initialValue' matches the input 'value', which defaults to 'on' in the browser.
```tsx
import { useForm } from '@conform-to/react';
function Example() {
const [form, fields] = useForm();
return (
);
}
```
--------------------------------
### Setup Array Fields with Conform
Source: https://github.com/edmundhung/conform/blob/main/docs/complex-structures.md
Use `getFieldList()` to manage array fields. Field names are automatically inferred (e.g., `tasks[0]`, `tasks[1]`). This method also supports intents for modifying list items.
```tsx
import { useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { z } from 'zod';
const schema = z.object({
tasks: z.array(z.string()),
});
function Example() {
const [form, fields] = useForm({
onValidate({ formData }) {
return parseWithZod(formData, { schema });
},
});
const tasks = fields.tasks.getFieldList();
return (
);
}
```
--------------------------------
### Checkbox Group Setup with Conform
Source: https://github.com/edmundhung/conform/blob/main/docs/checkbox-and-radio-group.md
This snippet demonstrates setting up a checkbox group. The 'initialValue' can be a string or an array. Errors for individual options are mapped to their index (e.g., 'answer[0]'). Use 'allErrors' to display all errors for the field.
```tsx
import { useForm } from '@conform-to/react';
function Example() {
const [form, fields] = useForm();
return (
);
}
```
--------------------------------
### Nested FormProvider Example
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/FormProvider.md
Illustrates how FormProvider can be nested to manage multiple forms within each other. useField will automatically look for the closest FormContext if no formId is provided.
```tsx
import { FormProvider, useForm } from '@conform-to/react';
function Field({ name, formId }) {
// useField will look for the closest FormContext if no formId is provided
const [meta] = useField(name, { formId });
return ;
}
function Parent() {
const [form, fields] = useForm({ id: 'parent' });
return (
);
}
function Child() {
const [form, fields] = useForm({ id: 'child' });
return (
{/* This will look for the form context with the id 'parent' instead */}
);
}
```
--------------------------------
### Directly Using Field Metadata for Select Props
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getSelectProps.md
This example shows how to manually set the props for a select element using field metadata, illustrating the boilerplate `getSelectProps` helps to reduce.
```tsx
// Before
function Example() {
return (
);
}
// After
function Example() {
return (
);
}
```
--------------------------------
### Using Custom Metadata with Headless UI ListBox
Source: https://github.com/edmundhung/conform/blob/main/examples/headless-ui/README.md
Apply the custom metadata defined via `configureForms` to Headless UI components for a cleaner, type-safe integration. This approach leverages the extended props for a more declarative setup.
```tsx
import {
ExampleListBox,
colorOptions,
} from "./src/App";
import { useForm } from "./src/forms";
const { fields } = useForm();
```
--------------------------------
### Valibot Schema with Custom Messages
Source: https://github.com/edmundhung/conform/blob/main/docs/api/valibot/conformValibotMessage.md
This example demonstrates how to create Valibot schemas for both server and client actions using `conformValibotMessage`. It shows how to conditionally skip validation or fallback to server validation based on the intent and specific field requirements.
```tsx
import type { Intent } from '@conform-to/react';
import { useForm } from '@conform-to/react';
import { parseWithValibot, conformValibotMessage } from '@conform-to/valibot';
import {
check,
forward,
forwardAsync,
object,
partialCheck,
partialCheckAsync,
pipe,
pipeAsync,
string,
} from 'valibot';
function createBaseSchema(intent: Intent | null) {
return object({
email: pipe(
string('Email is required'),
// When not validating email, leave the email error as it is.
check(
() =>
intent === null ||
(intent.type === 'validate' && intent.payload.name === 'email'),
conformValibotMessage.VALIDATION_SKIPPED,
),
),
password: string('Password is required'),
});
}
function createServerSchema(
intent: Intent | null,
options: { isEmailUnique: (email: string) => Promise },
) {
return pipeAsync(
createBaseSchema(intent),
forwardAsync(
partialCheckAsync(
[['email']],
async ({ email }) => options.isEmailUnique(email),
'Email is already used',
),
['email'],
),
);
}
function createClientSchema(intent: Intent | null) {
return pipe(
createBaseSchema(intent),
forward(
// If email is specified, fallback to server validation to check its uniqueness.
partialCheck(
[['email']],
() => false,
conformValibotMessage.VALIDATION_UNDEFINED,
),
['email'],
),
);
}
export async function action({ request }) {
const formData = await request.formData();
const submission = await parseWithValibot(formData, {
schema: (intent) =>
createServerSchema(intent, {
isEmailUnique: async (email) => {
// Query your database to check if the email is unique
},
}),
});
// Send the submission back to the client if the status is not successful
if (submission.status !== 'success') {
return submission.reply();
}
// ...
}
function ExampleForm() {
const [form, { email, password }] = useForm({
onValidate({ formData }) {
return parseWithValibot(formData, {
schema: (intent) => createClientSchema(intent),
});
},
});
// ...
}
```
--------------------------------
### Client-Server Form Validation with Conform and Zod
Source: https://github.com/edmundhung/conform/blob/main/docs/overview.md
This example demonstrates a full client-server form validation flow using Conform and Zod. It includes a server action handler for processing form data and a client component for rendering the form and handling user input. Configure validation timing with `shouldValidate` and provide server-side results via `lastResult`.
```tsx
import { useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { z } from 'zod';
import { login } from './your-auth-library';
import { useActionResult, redirect } from './your-server-framework';
// Define a schema for your form
const schema = z.object({
username: z.string(),
password: z.string(),
});
// Optional: Server action handler
export async function action({ request }) {
const formData = await request.formData();
const submission = parseWithZod(formData, {
schema
});
// Send the submission back to the client if the status is not successful
if (submission.status !== 'success') {
return submission.reply();
}
const session = await login(submission.value);
// Send the submission with addional error message if login fails
if (!session) {
return submission.reply({
formErrors: ['Incorrect username or password'],
});
}
return redirect('/dashboard');
}
// Client form component
export default function LoginForm() {
// Grab the last submission result if you have defined a server action handler
// This could be `useActionData()` or `useFormState()` depending on the framework
const lastResult = useActionResult();
const [form, fields] = useForm({
// Configure when each field should be validated
shouldValidate: 'onBlur',
// Optional: Required only if you're validating on the server
lastResult,
// Optional: Client validation. Fallback to server validation if not provided
onValidate({
formData
}) {
return parseWithZod(formData, {
schema
});
},
});
return (
);
}
```
--------------------------------
### Get Yup constraint
Source: https://github.com/edmundhung/conform/blob/main/docs/upgrading-v1.md
Use 'getYupConstraint' to get schema constraints from Yup schemas. This replaces 'getFieldsetConstraint'.
```typescript
// getFieldsetConstraint -> getYupConstraint
```
--------------------------------
### Get Zod constraint
Source: https://github.com/edmundhung/conform/blob/main/docs/upgrading-v1.md
Use 'getZodConstraint' to get schema constraints from Zod schemas. This replaces 'getFieldsetConstraint'.
```typescript
// getFieldsetConstraint -> getZodConstraint
```
--------------------------------
### Get Input Props
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getInputProps.md
A basic usage of getInputProps to retrieve accessibility props for an input field.
```tsx
const props = getInputProps(meta, options);
```
--------------------------------
### Custom Helper Creation
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getInputProps.md
Guidance on creating custom helper functions for non-native input components.
```APIDOC
### Make your own helper
The helper is designed for the native input elements. If you need to use a custom component, you can always make your own helpers.
```
--------------------------------
### Basic Usage of getSelectProps
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getSelectProps.md
Import `getSelectProps` from `@conform-to/react` and use it with your field metadata to get the necessary props for a select element.
```tsx
import { useForm, getSelectProps } from '@conform-to/react';
function Example() {
const [form, fields] = useForm();
return ;
}
```
--------------------------------
### useForm Tips
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/useForm.md
Additional usage tips and best practices for the useForm hook.
```APIDOC
## Tips
### Client validation is optional
Conform supports live validation (i.e. validate when the user leaves the input or types) without client validation. This is useful to avoid shipping the validation code in the client bundle. But please keep in mind network latency and how frequently your users might hit the server especially if you are revalidating every time they type.
### Automatic form reset when `id` is changed
You can pass a different `id` to the `useForm` hook to reset the form. This is useful when you are navigating to another page with the same form. (e.g. `/articles/foo` -> `/articles/bar`)
```tsx
interface Article {
id: string;
title: string;
content: string;
}
function EditArticleForm({ defaultValue }: { defaultValue: Article }) {
const [form, fields] = useForm({
id: `article-${defaultValue.id}`,
defaultValue,
});
// ...
}
```
```
--------------------------------
### Get Fieldset Props
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getFieldsetProps.md
Use getFieldsetProps to retrieve all necessary props for an accessible fieldset element. This is a convenience function to reduce boilerplate.
```tsx
const props = getFieldsetProps(meta, options);
```
--------------------------------
### Get Textarea Props
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/getTextareaProps.md
This is the basic usage of the getTextareaProps helper function. It takes field metadata and returns the necessary props for an accessible textarea element.
```tsx
const props = getTextareaProps(meta, options);
```
--------------------------------
### useFormMetadata Hook Usage
Source: https://github.com/edmundhung/conform/blob/main/docs/api/react/useFormMetadata.md
This snippet demonstrates the basic usage of the useFormMetadata hook in a React component. It shows how to import the hook and call it with a formId to get the form metadata.
```APIDOC
## useFormMetadata Hook
A React hook that returns the form metadata by subscribing to the context set on the [FormProvider](./FormProvider.md).
```tsx
const form = useFormMetadata(formId);
```
### Parameters
#### `formId`
The id attribute that is set on the form element.
### Returns
#### `form`
The form metadata. It is the same object as the one returned by the [useForm](./useForm.md) hook.
```
--------------------------------
### Using Zod v4
Source: https://github.com/edmundhung/conform/blob/main/docs/api/zod/parseWithZod.md
Instructions on how to import parseWithZod when using Zod version 4 or later.
```APIDOC
### Using Zod v4
If you are using Zod v4, make sure to import from `@conform-to/zod/v4` instead of `@conform-to/zod`:
```tsx
import { parseWithZod } from '@conform-to/zod/v4';
import { z } from 'zod'; // or 'zod/mini'
```
Using the wrong import will result in an error like `Export named 'ZodEffects' not found in module`.
```
--------------------------------
### Manipulating Field Lists with Intent Buttons
Source: https://github.com/edmundhung/conform/blob/main/docs/intent-button.md
Demonstrates how to use `insert`, `remove`, and `reorder` intents from Conform to manage an array of tasks. This is useful for dynamic lists where users can add, delete, or reorder items.
```tsx
import { useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { z } from 'zod';
const todosSchema = z.object({
title: z.string(),
tasks: z.array(z.string()),
});
export default function Tasks() {
const [form, fields] = useForm({
onValidate({ formData }) {
return parseWithZod(formData, { schema: todosSchema });
},
shouldValidate: 'onBlur',
});
const tasks = fields.tasks.getFieldList();
return (
);
}
```
--------------------------------
### Spread accessible props onto