Actions vs route handlers

Server Actions for your own UI, Route Handlers for everyone else.

From next-best-practices, route-handlers.md.

Sort the use cases

Drag each card into a bin, or click to cycle.

Server Action

POST only · internal

Route Handler

any method · public URL

Both work

pick by caller

0 of 8 sorted

// app/posts/new/page.tsx
import { createPost } from './actions'

export default function NewPost() {
  return (
    <form action={createPost}>
      <input name="title" />
      <button>Publish</button>
    </form>
  )
}

// app/api/webhooks/stripe/route.ts
export async function POST(request: Request) {
  const event = await request.json()
  await handleStripeEvent(event)
  return Response.json({ received: true })
}

Turn JavaScript off

Pick a JavaScript state, then publish on both sides.

fetch() in onClickBad
/posts/new
      <button onClick={() => fetch(…)}>
      <form action>Good
      /posts/new
          'use server'
          // app/posts/new/actions.ts
          'use server'
          import { revalidatePath } from 'next/cache'
          
          export async function createPost(formData: FormData) {
            const title = String(formData.get('title') ?? '')
            await db.post.create({ data: { title } })
            revalidatePath('/posts')
          }
          
          // submit-button.tsx
          'use client'
          import { useFormStatus } from 'react-dom'
          
          export function SubmitButton() {
            const { pending } = useFormStatus()
            return <button disabled={pending}>{pending ? 'Publishing…' : 'Publish'}</button>
          }

          Put route.ts next to page.tsx

          Add the route, then move it. Try a request.

          File treeClean
            Request

            GET POST PUT PATCH DELETE HEAD OPTIONS
            app/
            ├── users/
            │   └── page.tsx        # /users (page)
            └── api/
                └── users/
                    └── route.ts    # /api/users (API)

            Why it matters, pros and cons

            Why it matters

            An Action is a function your UI calls; a Route Handler is a public URL.

            Pros

            • No hand-written endpoint for your own mutations.
            • Forms submit before JavaScript loads.
            • Route Handler GETs can be HTTP-cached.

            Cons

            • Actions are POST only, so no HTTP caching.
            • No stable URL for webhooks or mobile apps.
            • Actions are public endpoints; authenticate inside them.