React 19 and Next.js 15 are bringing radical changes to how we build modern web apps, especially with Server Actions and the new useActionState
hook. But working with forms can still feel verbose, repetitive, and error-prone. That's why I built next-form-action
: a lightweight, type-safe utility to streamline form handling in full-stack React apps.
In this post, I'll show you how next-form-action
makes working with forms using useActionState
dramatically simpler and more scalable.
The Problem: Boilerplate and Scattered Logic
Here's what a typical login form looks like using useActionState
without any helper library:
'use client'
import { useActionState } from 'react'
import { redirect } from 'next/navigation'
async function login(prevState, formData) {
const email = formData.get('email') as string
const password = formData.get('password') as string
if (!email || !password) {
return { success: false, message: 'Email and password are required' }
}
try {
const user = await authenticate(email, password)
redirect('/dashboard')
} catch {
return { success: false, message: 'Invalid credentials' }
}
}
export default function LoginForm() {
const [state, formAction, isPending] = useActionState(login, { success: true, message: '' })
return (
<form action={formAction} className="space-y-4">
<input name="email" type="email" required />
<input name="password" type="password" required />
{state.message && <p className="text-red-500">{state.message}</p>}
<button disabled={isPending}>{isPending ? 'Logging in…' : 'Login'}</button>
</form>
)
}
It's not bad, but:
- You have to manually manage state shape
- You handle errors and redirects manually
- You miss lifecycle events like
onSubmit
oronSuccess
- Reusability and DX could be better
The Solution: next-form-action
With next-form-action
, you encapsulate the logic cleanly into an action + hook pair. Here's the same login form, refactored using the library:
Step 1: Define Your Action
// actions/login.ts
import { createAction, error, success } from 'next-form-action'
export const loginAction = createAction('login', async (state, formData) => {
const email = formData.get('email') as string
const password = formData.get('password') as string
if (!email || !password) {
error('Email and password are required')
}
try {
const user = await authenticate(email, password)
success('Login successful!', { redirect: '/dashboard' })
} catch {
error('Invalid credentials')
}
})
✨ Highlights:
- Built-in
redirect()
support viasuccess()
- Simplified error management using
error()
Step 2: Use in the Component
'use client'
import { useAction } from 'next-form-action'
import { loginAction } from './actions/login'
export default function LoginForm() {
const { Form, FormError, isPending } = useAction(loginAction)
return (
<Form className="space-y-4">
<input name="email" type="email" required />
<input name="password" type="password" required />
<FormError className="text-red-500" />
<button type="submit" disabled={isPending}>
{isPending ? 'Logging in…' : 'Login'}
</button>
</Form>
)
}
✅ Zero boilerplate. Type safety. Redirects handled. Better separation of concerns.
🧠 Built-in
FormError
handles dynamic error display based on action response.
Key Features
- ✅ Type-safe form actions with full TS support
- 🎣 Hooks-based API with built-in
Form
,FormError
, and lifecycle callbacks - ⚙️ Built-in error/redirect/refresh support
-
redirect('/path')
works natively inside actions -
notFound()
automatically triggers Next.js 404 - Refresh current page with
{ refresh: true }
-
- 📦 Lightweight & framework-native – no runtime deps, no magic
- 🧩 Perfect for App Router and Server Actions
Real-World Use Cases
next-form-action
shines in real-world apps where:
- ✅ You want reliable forms with built-in error handling and automatic error display
- 🔄 You want clean, declarative redirects and refresh behavior after actions
- 📊 You want to plug into analytics or logging via
onFormSubmit
/onFormError
- 📦 You want to reduce boilerplate and unify form structure across your app
Requirements
- React 19+
- Next.js 15+ with App Router
- TypeScript 5+
Try It Now
Install it with your favorite package manager:
npm install next-form-action
Explore the docs and source:
- GitHub: https://github.com/mattiamalonni/next-form-action
- NPM: https://www.npmjs.com/package/next-form-action
Final Thoughts
Modern full-stack React deserves ergonomic form handling. next-form-action
brings the DX you expect from tools like react-hook-form
, while embracing the new world of useActionState
and Server Actions.
If you're building anything with React 19 or Next.js 15, give it a shot — and say goodbye to fragile forms and verbose logic.
If you liked this, consider starring the repo, or sharing the library with someone working on Next.js forms. ❤️
Top comments (0)