### 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 (
{errors.name && {errors.name.message}} {errors.email && {errors.email.message}}
); } ``` -------------------------------- ### 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
); } ``` -------------------------------- ### 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 (
{errors.email && {errors.email.message}} {errors.password && {errors.password.message}}
); } ``` -------------------------------- ### 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 (