stackademic

The leading education platform for anyone with an interest in software development.

Server Actions

Run server-side mutations directly from components without building API endpoints

Overview

Server Actions are async functions that run on the server and can be invoked from forms or event handlers. Marked with the 'use server' directive, they let you handle mutations without manually creating API routes. They integrate with progressive enhancement, work without JavaScript, and can revalidate cached data after writing.

Syntax / Usage

Define an action with 'use server' at the top of the function or file, then pass it to a form's action prop.

// app/actions.ts
'use server'

import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  await db.post.create({ data: { title } })
  revalidatePath('/posts')
  redirect('/posts')
}
// app/posts/new/page.tsx (Server Component)
import { createPost } from '@/app/actions'

export default function NewPostPage() {
  return (
    <form action={createPost}>
      <input name="title" required />
      <button type="submit">Create</button>
    </form>
  )
}

Examples

Handle pending UI state from a Client Component with useFormStatus:

'use client'
import { useFormStatus } from 'react-dom'

export function SubmitButton() {
  const { pending } = useFormStatus()
  return <button disabled={pending}>{pending ? 'Saving…' : 'Save'}</button>
}

Return validation errors with useActionState:

'use client'
import { useActionState } from 'react'
import { subscribe } from '@/app/actions'

export function Newsletter() {
  const [state, action] = useActionState(subscribe, { error: null })
  return (
    <form action={action}>
      <input name="email" type="email" />
      {state.error && <p>{state.error}</p>}
      <button>Subscribe</button>
    </form>
  )
}

Common Mistakes

  • Forgetting the 'use server' directive, so the function runs on the client
  • Trusting formData input without server-side validation
  • Not calling revalidatePath or revalidateTag after a mutation, leaving stale UI
  • Importing a Server Action into a Client Component file that lacks 'use server' in the action module
  • Returning non-serializable values from an action

See Also

nextjs-route-handlers nextjs-data-fetching server-components