v4·hookform-action-standalone
Standalone Adapter
Use hookform-action in any React app — Vite, Remix, Astro, or plain SPAs. No Next.js or Server Actions required.
Installation
npm install hookform-action-standalone react-hook-form zod # or pnpm add hookform-action-standalone react-hook-form zod
hookform-action-standalone depends on hookform-action-core (core) as a regular dependency — it's installed automatically.
Basic Usage
Instead of passing a Server Action as the first argument, pass an options object with a submit function:
import { useActionForm } from 'hookform-action-standalone'
import { z } from 'zod'
const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Minimum 8 characters'),
})
export function LoginForm() {
const {
register,
handleSubmit,
formState: { errors, isPending, isSubmitSuccessful },
} = useActionForm({
submit: async (data) => {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
})
return res.json()
},
schema,
validationMode: 'onChange',
defaultValues: { email: '', password: '' },
})
if (isSubmitSuccessful) {
return <p className="text-green-500">✓ Logged in!</p>
}
return (
<form onSubmit={handleSubmit()}>
<input {...register('email')} placeholder="Email" />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('password')} type="password" placeholder="Password" />
{errors.password && <span>{errors.password.message}</span>}
<button disabled={isPending}>
{isPending ? 'Signing in...' : 'Sign In'}
</button>
</form>
)
}Next.js vs Standalone
| Feature | Next.js Adapter | Standalone Adapter |
|---|---|---|
| Import | hookform-action | hookform-action-standalone |
| Signature | useActionForm(action, options) | useActionForm({ submit, ...options }) |
| formAction | ✅ Provided | ❌ Not applicable |
| FormData | Auto-converts | Not needed |
| Error mapping | ✅ Identical | ✅ Identical |
| Optimistic UI | ✅ Identical | ✅ Identical |
| Persistence | ✅ Identical | ✅ Identical |
| Client validation | ✅ Identical | ✅ Identical |
| DevTools | ✅ Compatible | ✅ Compatible |
Vite Setup
# Create a Vite + React + TypeScript project npm create vite@latest my-app -- --template react-ts cd my-app npm install hookform-action-standalone react-hook-form zod npm install hookform-action-devtools # optional
No additional Vite configuration needed. The library ships ESM + CJS and works out of the box.
Optimistic UI Example
import { useActionForm } from 'hookform-action-standalone'
function EditTodo({ todo }: { todo: Todo }) {
const { register, handleSubmit, optimistic } = useActionForm({
submit: async (data) => {
const res = await fetch(`/api/todos/${todo.id}`, {
method: 'PATCH',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
})
return res.json()
},
defaultValues: { title: todo.title },
optimisticKey: `todo-${todo.id}`,
optimisticInitial: todo,
optimisticData: (current, values) => ({ ...current, ...values }),
})
return (
<form onSubmit={handleSubmit()}>
<h2>{optimistic?.data?.title}</h2>
<input {...register('title')} />
<button type="submit">Save</button>
</form>
)
}Bundle Size
| Package | ESM (minified) | Peer deps |
|---|---|---|
| hookform-action-core (core) | ~4 KB | react, react-hook-form, zod |
| hookform-action-standalone | ~1 KB (adapter only) | react, react-hook-form, zod |
| hookform-action-devtools | ~3 KB | react, react-hook-form |
All packages are tree-shakeable (sideEffects: false). DevTools is only included when imported — typically behind a process.env.NODE_ENV guard.