### Install Project Dependencies
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/test-apps/react-router/README.md
Run this command to install all necessary project dependencies.
```bash
npm install
```
--------------------------------
### Example Middleware Setup with Options
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/middleware.md
Configure `unstable_extractFormDataMiddleware` in your root route, specifying whether to preserve stringified form data values.
```typescript
// root.tsx
import { unstable_extractFormDataMiddleware } from 'remix-hook-form/middleware';
import type { Route } from './+types/root';
export const unstable_middleware = [
unstable_extractFormDataMiddleware({
preserveStringified: false,
}),
];
export default function Root({ children }: Route.ComponentProps) {
return (
{children}
);
}
```
--------------------------------
### Usage: Handling Both GET and POST
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/parseFormData.md
Provides an example of how to handle form data from both GET (search params) and POST (request body) methods.
```APIDOC
## Handling Both GET and POST
### Description
This example shows how to conditionally parse data based on the HTTP method. For GET requests, it uses `getFormDataFromSearchParams`, and for POST/PUT/PATCH requests, it uses `parseFormData`.
### Code
```typescript
import { parseFormData, getFormDataFromSearchParams } from 'remix-hook-form';
export const action = async ({ request }: ActionFunctionArgs) => {
let data;
if (request.method === 'GET') {
// GET requests use search params
data = getFormDataFromSearchParams(request);
} else {
// POST/PUT/PATCH requests use form data
data = await parseFormData(request);
}
// Process data...
};
```
```
--------------------------------
### Basic Usage of Remix Hook Form
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Example demonstrating basic form setup with validation using Zod and react-hook-form in a Remix.run application. This example works with or without JavaScript.
```typescript
import { useRemixForm, getValidatedFormData } from "remix-hook-form";
import { Form } from "react-router";
import { zodResolver } from "@hookform/resolvers/zod";
import * as zod from "zod";
import type { Route } from "./+types/home";
const schema = zod.object({
name: zod.string().min(1),
email: zod.string().email().min(1),
});
type FormData = zod.infer;
const resolver = zodResolver(schema);
export const action = async ({ request }: Route.ActionArgs) => {
const { errors, data, receivedValues: defaultValues } =
await getValidatedFormData(request, resolver);
if (errors) {
// The keys "errors" and "defaultValues" are picked up automatically by useRemixForm
return { errors, defaultValues };
}
// Do something with the data
return data;
};
export default function MyForm() {
const {
handleSubmit,
formState: { errors },
register,
} = useRemixForm({
mode: "onSubmit",
resolver,
});
return (
);
}
```
--------------------------------
### Install remix-hook-form
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Install the remix-hook-form package and react-hook-form using npm.
```bash
npm install remix-hook-form react-hook-form
```
--------------------------------
### Start Development Server
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/test-apps/react-router/README.md
Starts the development server with Hot Module Replacement (HMR) enabled for rapid development.
```bash
npm run dev
```
--------------------------------
### Install Zod and Hookform Resolvers
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Install additional dependencies for schema validation with Zod and react-hook-form.
```bash
npm install zod @hookform/resolvers
```
--------------------------------
### Handle Both GET and POST Requests
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/parseFormData.md
This example shows how to conditionally parse data based on the HTTP method. For GET requests, it uses `getFormDataFromSearchParams`, and for POST/PUT/PATCH, it uses `parseFormData`.
```typescript
import { parseFormData, getFormDataFromSearchParams } from 'remix-hook-form';
export const action = async ({ request }: ActionFunctionArgs) => {
let data;
if (request.method === 'GET') {
// GET requests use search params
data = getFormDataFromSearchParams(request);
} else {
// POST/PUT/PATCH requests use form data
data = await parseFormData(request);
}
// Process data...
};
```
--------------------------------
### Pagination
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/getFormDataFromSearchParams.md
Example demonstrating how to extract pagination parameters like `page` and `limit` from search parameters.
```APIDOC
## getFormDataFromSearchParams for Pagination
### Description
Extract pagination-related query parameters such as `page` and `limit` using `getFormDataFromSearchParams` to control data fetching.
### Method
GET
### Endpoint
/
### Parameters
#### Query Parameters
- **request** (Request) - Required - The incoming request object.
### Response
#### Success Response (200)
- **page** (number) - The current page number, defaults to 1.
- **limit** (number) - The number of items per page, defaults to 10.
### Request Example
```typescript
export const loader = async ({ request }: LoaderFunctionArgs) => {
const formData = getFormDataFromSearchParams<{ page?: number; limit?: number }>(request);
const page = formData.page ?? 1;
const limit = formData.limit ?? 10;
const items = await fetchItems({
offset: (page - 1) * limit,
limit,
});
return { items, page, limit };
};
```
```
--------------------------------
### Middleware Setup
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Set up the unstable_extractFormDataMiddleware in your root.tsx file to enable middleware mode for form data extraction.
```APIDOC
## Middleware mode
From v7 you can use middleware to extract the form data and access it anywhere in your actions and loaders.
All you have to do is set it up in your `root.tsx` file like this:
```ts
import { unstable_extractFormDataMiddleware } from "remix-hook-form/middleware";
export const unstable_middleware = [unstable_extractFormDataMiddleware()];
```
And then access it in your actions and loaders like this:
```ts
import { getFormData, getValidatedFormData } from "remix-hook-form/middleware";
export const loader = async ({ context }: LoaderFunctionArgs) => {
const searchParamsFormData = await getFormData(context);
return { result: "success" };
};
export const action = async ({ context }: ActionFunctionArgs) => {
// OR: const formData = await getFormData(context);
const { data, errors, receivedValues } = await getValidatedFormData(
context,
resolver,
);
if (errors) {
return { errors, receivedValues };
}
return { result: "success" };
};
```
```
--------------------------------
### Complete Example: getFormData and validateFormData
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/getFormData.md
A full example demonstrating the integration of getFormData for initial data extraction and validateFormData with a Zod schema for validation within a Remix action function.
```typescript
import { getFormData, validateFormData } from 'remix-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import type { ActionFunctionArgs } from 'react-router';
const schema = z.object({
email: z.string().email(),
age: z.number().int().positive(),
newsletter: z.boolean().optional(),
});
const resolver = zodResolver(schema);
export const action = async ({ request }: ActionFunctionArgs) => {
// Extract without validation
const { receivedValues } = await getFormData(request);
// Log for audit
console.log('Received form data:', receivedValues);
// Validate manually
const { data, errors } = await validateFormData(receivedValues, resolver);
if (errors) {
return { errors, defaultValues: receivedValues };
}
// Process validated data
await updateUser(data);
return { success: true };
};
```
--------------------------------
### Example Form Data Object
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Represents the data structure before serialization for submission to the server.
```typescript
const formData = {
name: "123",
age: 30,
hobbies: ["Reading", "Writing", "Coding"],
boolean: true,
a: null,
// this gets omitted because it's undefined
b: undefined,
numbers: [1, 2, 3],
other: {
skills: ["testing", "testing"],
something: "else",
},
};
```
--------------------------------
### Basic Form Setup with Zod Validation
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/configuration.md
Configure useRemixForm with Zod for schema validation. Set validation mode to 'onBlur' and enable focusing the first errored field.
```typescript
import {
useRemixForm
} from 'remix-hook-form';
import {
zodResolver
} from '@hookform/resolvers/zod';
import * as z from 'zod';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
const {
handleSubmit,
register
} = useRemixForm({
resolver: zodResolver(schema),
mode: 'onBlur', // Validate when field loses focus
shouldFocusError: true, // Focus first errored field
});
```
--------------------------------
### Example of Double Stringified String
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Illustrates how a string value is double stringified when sent to the server by default.
```typescript
const string = "'123'";
```
--------------------------------
### Basic Form Submission with useRemixForm
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/useRemixForm.md
This example demonstrates a basic form submission using useRemixForm with Zod validation. It includes the action handler for server-side validation and the React component for the form.
```typescript
import { useRemixForm, getValidatedFormData } from 'remix-hook-form';
import { Form } from 'react-router';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
const schema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
const resolver = zodResolver(schema);
type FormData = z.infer;
export const action = async ({ request }: Route.ActionArgs) => {
const { errors, data, receivedValues } = await getValidatedFormData(
request,
resolver
);
if (errors) {
return { errors, defaultValues: receivedValues };
}
// Handle success
return { success: true };
};
export default function MyForm() {
const { handleSubmit, register, formState: { errors } } = useRemixForm({
resolver,
});
return (
);
}
```
--------------------------------
### Middleware Mode Setup
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/README.md
Demonstrates how to enable middleware mode for remix-hook-form in your `root.tsx` file. This mode allows for form data extraction within middleware.
```typescript
export const unstable_middleware = [
unstable_extractFormDataMiddleware(),
];
```
--------------------------------
### Middleware Setup in root.tsx
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/configuration.md
Configure the `unstable_extractFormDataMiddleware` in your `root.tsx` file to manage form data extraction. The `preserveStringified` option defaults to `false`.
```typescript
import { unstable_extractFormDataMiddleware } from 'remix-hook-form/middleware';
export const unstable_middleware = [
unstable_extractFormDataMiddleware({
preserveStringified: false, // Default
}),
];
```
--------------------------------
### From GET Request (Search Params)
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/getFormData.md
Extracts data from the search parameters of a GET request URL. Useful for processing query strings.
```typescript
import { getFormData } from 'remix-hook-form';
export const loader = async ({ request }: LoaderFunctionArgs) => {
// URL: /?search=hello&page=1
const { receivedValues } = await getFormData(request);
return {
query: receivedValues.search,
page: receivedValues.page,
};
};
```
--------------------------------
### RemixFormProvider Setup
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/README.md
Utilize `RemixFormProvider` to provide form methods and context down to nested components. It mirrors `react-hook-form`'s `FormProvider` but includes additional error and submit information.
```jsx
export default function Form() {
const methods = useRemixForm();
return (
// pass all methods into the context
);
}
```
--------------------------------
### Multi-Step Form with Conditional Fields
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/RemixFormProvider.md
Illustrates building a multi-step form where fields are conditionally rendered based on form input. This example uses `watch` from `useRemixFormContext` to monitor changes and display additional fields like 'companyName' only when 'formType' is 'business'.
```typescript
const Step1Fields = () => {
const { register, watch } = useRemixFormContext();
const formType = watch('formType');
return (
{formType === 'business' && (
)}
);
};
export default function MyForm() {
const methods = useRemixForm({
resolver,
});
return (
);
}
```
--------------------------------
### Basic GET Form Data Extraction
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/getFormDataFromSearchParams.md
Extracts form data from the request's search parameters for GET requests. Assumes simple key-value pairs.
```typescript
import { getFormDataFromSearchParams } from 'remix-hook-form';
import type { LoaderFunctionArgs } from 'react-router';
export const loader = async ({ request }: LoaderFunctionArgs) => {
// URL: /?name=John&email=john@example.com
const formData = getFormDataFromSearchParams(request);
return { formData };
// { name: 'John', email: 'john@example.com' }
};
```
--------------------------------
### Import Core Hooks and Utilities from Remix Hook Form
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/README.md
Import the main hooks and form data utilities for use in your Remix application. Ensure you have react-hook-form installed as a peer dependency.
```typescript
import {
useRemixForm,
RemixFormProvider,
useRemixFormContext,
parseFormData,
createFormData,
getValidatedFormData,
validateFormData,
getFormDataFromSearchParams,
generateFormData,
type UseRemixFormOptions,
type UseRemixFormReturn,
} from 'remix-hook-form';
```
--------------------------------
### Basic Form with Zod Validation
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/README.md
Demonstrates a basic form setup using remix-hook-form with Zod for schema validation. This snippet shows how to integrate validation into your Remix actions and React components.
```typescript
import { useRemixForm, getValidatedFormData } from 'remix-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Form } from 'react-router';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
const resolver = zodResolver(schema);
type FormData = z.infer;
export const action = async ({ request }: Route.ActionArgs) => {
const { errors, data, receivedValues } = await getValidatedFormData(
request,
resolver
);
if (errors) return { errors, defaultValues: receivedValues };
// Process data
return { success: true };
};
export default function LoginForm() {
const { handleSubmit, register, formState: { errors } } = useRemixForm({
resolver,
});
return (
);
}
```
--------------------------------
### Perform Custom Processing After Parsing
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/parseFormData.md
After parsing form data, you can perform additional processing. This example shows how to merge the parsed data with other information, like user IDs or timestamps, before returning it.
```typescript
import { parseFormData } from 'remix-hook-form';
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await parseFormData(request);
// Additional processing after parsing
const processed = {
...formData,
userId: currentUser.id,
processedAt: new Date(),
};
return { received: processed };
};
```
--------------------------------
### Complex Form Structure Example
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/generateFormData.md
Illustrates the creation of a deeply nested and complex object structure, combining nested objects, arrays, and dynamic array appends from FormData.
```typescript
const formData = new FormData();
// User info
formData.append('user.firstName', 'John');
formData.append('user.lastName', 'Doe');
// Address
formData.append('user.address.street', '123 Main St');
formData.append('user.address.city', 'New York');
// Phone numbers
formData.append('user.phones[0].number', '555-1234');
formData.append('user.phones[0].type', 'home');
formData.append('user.phones[1].number', '555-5678');
formData.append('user.phones[1].type', 'work');
// Tags
formData.append('user.tags[]', 'vip');
formData.append('user.tags[]', 'verified');
const result = generateFormData(formData);
// {
// user: {
// firstName: 'John',
// lastName: 'Doe',
// address: {
// street: '123 Main St',
// city: 'New York'
// },
// phones: [
// { number: '555-1234', type: 'home' },
// { number: '555-5678', type: 'work' }
// ],
// tags: ['vip', 'verified']
// }
// }
```
--------------------------------
### Get Form Data and Validation in Remix Route
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/middleware.md
Demonstrates how to use `getFormData` to retrieve raw form data and `getValidatedFormData` with a Zod schema resolver to validate and process form submissions within Remix route handlers. This is useful for both loader and action functions.
```typescript
import { getValidatedFormData } from 'remix-hook-form/middleware';
import { getFormData } from 'remix-hook-form/middleware';
import { useRemixForm } from 'remix-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
import { Form } from 'react-router';
const schema = z.object({
email: z.string().email(),
message: z.string().min(1),
});
const resolver = zodResolver(schema);
type FormData = z.infer;
export const loader = async ({ context }: LoaderFunctionArgs) => {
// Get form data without validation
const formData = await getFormData(context);
console.log('Form data in loader:', formData);
return { ok: true };
};
export const action = async ({ context }: ActionFunctionArgs) => {
// Get form data with validation
const { errors, data, receivedValues } = await getValidatedFormData(
context,
resolver
);
if (errors) {
return { errors, defaultValues: receivedValues };
}
// Process form
await sendEmail(data);
return { success: true };
};
export default function ContactForm() {
const { handleSubmit, register, formState: { errors } } = useRemixForm({
resolver,
});
return (
);
}
```
--------------------------------
### Build for Production
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/test-apps/react-router/README.md
Creates a production-ready build of the application, optimized for deployment.
```bash
npm run build
```
--------------------------------
### Docker Build and Run Commands
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/test-apps/react-router/README.md
Commands for building Docker images using different package managers and running the containerized application.
```bash
# For npm
docker build -t my-app .
# For pnpm
docker build -f Dockerfile.pnpm -t my-app .
# For bun
docker build -f Dockerfile.bun -t my-app .
# Run the container
docker run -p 3000:3000 my-app
```
--------------------------------
### In getValidatedFormData for GET Requests
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/api-reference/getFormDataFromSearchParams.md
Demonstrates how `getValidatedFormData` automatically utilizes `getFormDataFromSearchParams` for GET requests when used with a Zod schema resolver.
```APIDOC
## getValidatedFormData with GET Requests
### Description
The `getValidatedFormData` utility automatically detects GET requests and uses `getFormDataFromSearchParams` to extract and parse data from search parameters before validation.
### Method
GET
### Endpoint
/
### Parameters
#### Query Parameters
- **request** (Request) - Required - The incoming request object.
- **resolver** (Function) - Required - The resolver function (e.g., zodResolver) for data validation.
### Response
#### Success Response (200)
- **data** (object) - The validated data from the search parameters.
- **errors** (object) - An object containing validation errors, if any.
### Request Example
```typescript
import { getValidatedFormData } from 'remix-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
const schema = z.object({
search: z.string(),
page: z.number(),
filters: z.object({
category: z.string(),
sort: z.string(),
}).optional(),
});
const resolver = zodResolver(schema);
export const loader = async ({ request }: LoaderFunctionArgs) => {
// Automatically detects GET and extracts from search params
// URL: /?search=hello&page=1&filters.category=books&filters.sort=date
const { data, errors } = await getValidatedFormData(request, resolver);
if (errors) {
return { errors };
}
return { results: data };
};
```
```
--------------------------------
### Client-Side Form Handling with useRemixForm
Source: https://github.com/code-forge-io/remix-hook-form/blob/main/_autodocs/INDEX.md
Demonstrates client-side form handling using `useRemixForm` hook. Includes input registration and submission logic.
```typescript
const methods = useRemixForm({ resolver })