When building forms in Next.js 14+, it's tempting to convert entire forms to client components when you need interactive features like loading states. However, this approach sacrifices the performance benefits of Server Components.
But what if you have a form that needs a loading state on the submit button? You might be tempted to turn the whole form into a client component — but that's unnecessary!
The Better Approach: Keep your forms server-side and selectively use client components only where interactivity is required.
Key Benefits
- Performance: Server Components render faster and reduce JavaScript bundle size
- SEO: Better search engine optimization with server-side rendering
- Scalability: Reduced client-side processing load
- Maintainability: Clear separation between server and client logic
Best Practice: Use the useFormStatus()
hook to create minimal client components that handle only the interactive states you need, while keeping the core form logic on the server.
Submit button
'use client'
import { useFormStatus } from ' react-dom'
export default function Submit() {
const { pending } = useFormStatus()
return (
<button type="submit">
{pending ? <Loader /> : 'submit' }
</button>
)
}
Form
import Submit from './submit'
export default async function Form() {
async function action (formData: FormData) {
'use server'
const name = formData. get ( 'name' )
setTimeout (() => {
console. log (name )
}, 1000)
}
return (
<form action={action}>
<input type="text" name="name" />
<Submit />
</form>
)
}