What are Server Actions?
Server Actions are asynchronous functions that are executed on the server. They can be called from Server Components or Client Components to handle form submissions and data mutations.
Security Note: Server Actions create a public HTTP endpoint. Always validate inputs and check authorization inside the action.
Defining Actions
1. Inline in Server Components
// app/page.js (Server Component)
export default function Page() {
async function createPost(formData) {
'use server' // Directive is crucial
const title = formData.get('title')
await db.post.create({ data: { title } })
}
return (
<form action={createPost}>
<input type="text" name="title" />
<button type="submit">Create</button>
</form>
)
}
2. Separate File (Recommended for Reusability)
// app/actions.js
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
// Note: prevState is required when using useFormState
export async function createPost(prevState, formData) {
const title = formData.get('title')
// 1. Validate
if (!title) return { error: 'Title required' }
// 2. Mutate
await db.post.create({ data: { title } })
// 3. Revalidate Cache
revalidatePath('/posts')
// 4. Redirect
redirect('/posts')
}
Using in Client Components
You can import server actions into Client Components.
// app/ui/button.js
'use client'
import { createPost } from '@/app/actions'
export default function Button() {
return (
<button onClick={async () => {
await createPost(new FormData())
}}>
Click me
</button>
)
}
Pending States
Use useFormStatus to show loading indicators during form submission.
'use client'
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
)
}
Error Handling
Use useFormState (or useActionState in React 19) to handle return values and errors.
'use client'
import { useFormState } from 'react-dom'
import { createPost } from '@/app/actions'
const initialState = { message: null, errors: {} }
export default function Form() {
const [state, dispatch] = useFormState(createPost, initialState)
return (
<form action={dispatch}>
<input name="title" />
{state?.errors?.title && <p>{state.errors.title}</p>}
<button>Save</button>
</form>
)
}