- Notifications
You must be signed in to change notification settings - Fork0
Seamlessly integrate next-safe-action with react-hook-form.
License
next-safe-action/adapter-react-hook-form
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This adapter offers a way to seamlessly integratenext-safe-action withreact-hook-form.
- React >=
18.2.0
- Next.js >=
14.0.0
- next-safe-action >=
7.6.0
- react-hook-form >=
7.0.0
- @hookform/resolvers >=
3.0.0
npm i next-safe-action react-hook-form @hookform/resolvers @next-safe-action/adapter-react-hook-form
The best way to learn how to use this adapter is to take a look at the examples. Theapp in this repository shows you how to use theuseHookFormAction
anduseHookFormOptimisticAction
hooks:
This hook is a wrapper arounduseAction
from next-safe-action anduseForm
from react-hook-form that makes it much easier to use safe actions with react-hook-form. It also maps validation errors toFieldErrors
compatible with react-hook-form.
- First of all, we need a shared file to store our validation schema(s). In this case, the
loginSchema
Zod validator is exported fromvalidation.ts
:
import{z}from"zod";exportconstloginSchema=z.object({username:z.string().min(3).max(30),password:z.string().min(8).max(100),});
- Then, we can create our login action using
loginSchema
:
"use server";import{returnValidationErrors}from"next-safe-action";import{actionClient}from"@/lib/safe-action";import{loginSchema}from"./validation";import{checkCredentials}from"@/services/auth";exportconstloginAction=actionClient.schema(loginSchema).action(async({ parsedInput})=>{constvalid=awaitcheckCredentials(parsedInput.username,parsedInput.password);// If the credentials are invalid, return root validation error.if(!valid){returnValidationErrors(loginSchema,{_errors:["Invalid username or password"],});}return{successful:true,};});
- Finally, we can use
useHookFormAction
in our Client Component, by passing to it theloginSchema
andloginAction
declared above:
"use client";import{useHookFormAction}from"@next-safe-action/adapter-react-hook-form/hooks";import{zodResolver}from"@hookform/resolvers/zod";import{loginSchema}from"./validation";import{loginAction}from"./login-action";exportfunctionLoginForm(){const{ form, action, handleSubmitWithAction, resetFormAndAction}=useHookFormAction(loginAction,zodResolver(loginSchema),{actionProps:{},formProps:{},errorMapProps:{},});return<formonSubmit={handleSubmitWithAction}>...</form>;}
safeAction
: the safe action (required)hookFormResolver
: a react-hook-form validation resolver (required)props
: props foruseAction
,useForm
and error mapper (optional)
form
: the react-hook-form formaction
: the next-safe-action actionhandleSubmitWithAction
: a function that handles form submission by automatically executing the actionresetFormAndAction
: a function that resets the form and the action state
This hook is a wrapper arounduseOptimisticAction
from next-safe-action anduseForm
from react-hook-form that makes it much easier to use safe actions with react-hook-form. It also maps validation errors toFieldErrors
compatible with react-hook-form.
- First of all, we need a shared file to store our validation schema(s). In this case, the
addTodoSchema
Zod validator is exported fromvalidation.ts
:
import{z}from"zod";exportconstaddTodoSchema=z.object({newTodo:z.string().min(1).max(200),});
- Then, we can create our add todo action using
addTodoSchema
:
"use server";import{returnValidationErrors}from"next-safe-action";import{revalidatePath}from"next/cache";import{actionClient}from"@/lib/safe-action";import{addTodoSchema}from"./validation";import{badWordsCheck}from"@/utils";import{saveTodoInDb}from"@/services/db";exportconstaddTodoAction=actionClient.schema(addTodoSchema).action(async({ parsedInput})=>{constcontainsBadWords=badWordsCheck(parsedInput.newTodo);// If the todo conif(containsBadWords){returnValidationErrors(addTodoSchema,{newTodo:{_errors:["The todo contains bad words!"],},});}awaitsaveTodoInDb(parsedInput.newTodo);revalidatePath("/");return{newTodo:parsedInput.newTodo,};});
- Finally, we can use
useHookFormOptimisticAction
in our Client Component, by passing to it theaddTodoSchema
andaddTodoAction
declared above:
"use client";import{useHookFormOptimisticAction}from"@next-safe-action/adapter-react-hook-form/hooks";import{zodResolver}from"@hookform/resolvers/zod";import{addTodoSchema}from"./validation";import{addTodoAction}from"./addtodo-action";typeProps={todos:string[];};// Todos are passed from the parent Server Component and updated each time a new todo is added// thanks to the `revalidatePath` function called inside the action.exportfunctionAddTodoForm({ todos}:Props){const{ form, action, handleActionSubmit, resetFormAndAction}=useHookFormOptimisticAction(addTodoAction,zodResolver(addTodoSchema),{actionProps:{currentState:{todos,},updateFn:(state,input)=>{return{todos:[...state.todos,input.newTodo],};},},formProps:{},errorMapProps:{},});return<formonSubmit={handleActionSubmit}></form>;}
safeAction
: the safe action (required)hookFormResolver
: a react-hook-form validation resolver (required)props
: props foruseOptimisticAction
,useForm
and error mapper.actionProps.currentState
andactionProps.updateFn
are required by theuseOptimisticAction
hook used under the hood, the rest are optional. (required/optional)
form
: the react-hook-form formaction
: the next-safe-action actionhandleSubmitWithAction
: a function that handles form submission by automatically executing the actionresetFormAndAction
: a function that resets the form and the action state
For more control over the execution flow, you can use this hook to get back the memoized mapped validation errors of the action. It can be useful for cases when you need to use bothuseAction
anduseForm
in your Client Component, for a particular task, or when you want to create custom hooks.
We'll reuse the
loginSchema
andloginAction
from theuseHookFormAction
example here.Here's how you would use
useHookFormActionErrorMapper
in your Client Component:
"use client";import{useHookFormActionErrorMapper}from"@next-safe-action/adapter-react-hook-form/hooks";import{zodResolver}from"@hookform/resolvers/zod";import{loginSchema}from"./validation";import{loginAction}from"./login-action";import{useAction}from"next-safe-action/hooks";import{useForm}from"react-hook-form";import{Infer}from"next-safe-action/adapters/types";exportfunctionCustomForm(){constaction=useAction(loginAction);const{ hookFormValidationErrors}=useHookFormActionErrorMapper<typeofloginSchema>(action.result.validationErrors,{joinBy:"\n"});constform=useForm<Infer<typeofloginSchema>>({resolver:zodResolver(loginSchema),errors:hookFormValidationErrors,});return<formonSubmit={form.handleSubmit(action.executeAsync)}>...</form>;}
validationErrors
: next-safe-action object ofValidationErrors
, orundefined
(required)props
:joinBy
fromErrorMapperProps
type. It's used to determine how to join the error messages, if more than one is present in the errors array. It defaults to" "
(optional)
hookFormValidationErrors
: object of mapped errors withFieldErrors
type, compatible with react-hook-form
For more advanced stuff, you can directly use themapToHookFormErrors
function that is utilized under the hood to map next-safe-actionValidationErrors
to react-hook-form compatibleFieldErrors
.
import{mapToHookFormErrors}from"@next-safe-action/adapter-react-hook-form";import{loginAction}from"./login-action";importtype{loginSchema}from"./validation";asyncfunctionadvancedStuff(){constresult=awaitloginAction({username:"foo",password:"bar"});consthookFormValidationErrors=mapToHookFormErrors<typeofloginSchema>(result?.validationErrors,{joinBy:"\n"});// Do something with `hookFormValidationErrors`...}
validationErrors
: next-safe-action object ofValidationErrors
, orundefined
(required)props
:joinBy
fromErrorMapperProps
type. It's used to determine how to join the error messages, if more than one is present in the errors array. It defaults to" "
(optional)
- mapped errors: object of mapped errors with
FieldErrors
type, compatible with react-hook-form
Props formapToHookFormErrors
. Also used by the hooks.
exporttypeErrorMapperProps={joinBy?:string;};
Optional props foruseHookFormAction
anduseHookFormOptimisticAction
.
exporttypeHookProps<ServerError,SextendsStandardSchemaV1|undefined,CVE,Data,FormContext=any>={errorMapProps?:ErrorMapperProps;actionProps?:HookCallbacks<ServerError,S,CVE,Data>;formProps?:Omit<UseFormProps<InferInputOrDefault<S,any>,FormContext,InferOutputOrDefault<S,any>>,"resolver">;};
Type of the return object of theuseHookFormAction
hook.
exporttypeUseHookFormActionHookReturn<ServerError,SextendsStandardSchemaV1|undefined,CVE,Data,FormContext=any,>={action:UseActionHookReturn<ServerError,S,CVE,Data>;form:UseFormReturn<InferInputOrDefault<S,any>,FormContext,InferOutputOrDefault<S,any>>;handleSubmitWithAction:(e?:React.BaseSyntheticEvent)=>Promise<void>;resetFormAndAction:()=>void;};
Type of the return object of theuseHookFormOptimisticAction
hook.
exporttypeUseHookFormOptimisticActionHookReturn<ServerError,SextendsStandardSchemaV1|undefined,CVE,Data,State,FormContext=any,>=Omit<UseHookFormActionHookReturn<ServerError,S,CVE,Data,FormContext>,"action">&{action:UseOptimisticActionHookReturn<ServerError,S,CVE,Data,State>;};
You can use these utility types exported from the/hooks
path to infer the return types of the hooks.
Infer the type of the return object of theuseHookFormAction
hook.
exporttypeInferUseHookFormActionHookReturn<TextendsFunction,FormContext=any>=TextendsSafeActionFn<inferServerError,inferSextendsSchema|undefined,inferBASextendsreadonlySchema[],inferCVE,inferCBAVE,inferData>?UseHookFormActionHookReturn<ServerError,S,BAS,CVE,CBAVE,Data,FormContext>:never;
Infer the type of the return object of theuseHookFormOptimisticAction
hook.
exporttypeInferUseHookFormOptimisticActionHookReturn<TextendsFunction,State,FormContext=any>=TextendsSafeActionFn<inferServerError,inferSextendsSchema|undefined,inferBASextendsreadonlySchema[],inferCVE,inferCBAVE,inferData>?UseHookFormOptimisticActionHookReturn<ServerError,S,BAS,CVE,CBAVE,Data,State,FormContext>:never;
This project is released under theMIT License.
About
Seamlessly integrate next-safe-action with react-hook-form.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.