Skip to main content
after lets you schedule work to be executed after a response (or prerender) is finished. This is useful for tasks that should not block the response, such as logging, analytics, and other side effects. It can be used in Server Components (including generateMetadata), Server Functions, Route Handlers, and Middleware.
app/layout.tsx
import { after } from 'next/server'
import { log } from '@/app/utils'

export default function Layout({ children }: { children: React.ReactNode }) {
  after(() => {
    // Executes after the layout is rendered and sent to the user
    log()
  })
  return <>{children}</>
}

Parameters

callback
() => void | Promise<void>
required
A function to execute after the response or prerender is finished. Can be synchronous or asynchronous.

Duration

after runs for the platform’s default or configured max duration. You can configure the timeout with the maxDuration route segment config.

Good to know

  • after is not a Request-time API. Calling it does not make a route dynamic. If used in a static page, the callback runs at build time or when the page is revalidated.
  • after is executed even if the response did not complete successfully — including when an error is thrown or when notFound() or redirect() is called.
  • after calls can be nested. Utility functions can wrap after to add additional behavior.
  • Use React cache to deduplicate functions called inside after.

Using request APIs inside after

Whether you can use cookies() and headers() inside after depends on where after is called:

In Route Handlers and Server Functions

You can call cookies() and headers() directly inside the after callback:
app/api/route.ts
import { after } from 'next/server'
import { cookies, headers } from 'next/headers'
import { logUserAction } from '@/app/utils'

export async function POST(request: Request) {
  // Perform mutation...

  after(async () => {
    const userAgent = (await headers()).get('user-agent') || 'unknown'
    const sessionCookie =
      (await cookies()).get('session-id')?.value || 'anonymous'
    logUserAction({ sessionCookie, userAgent })
  })

  return Response.json({ status: 'success' })
}

In Server Components (pages and layouts)

Server Components cannot call cookies() or headers() inside the after callback. Read request data before after and pass the values in via closure:
app/page.tsx
import { after } from 'next/server'
import { cookies, headers } from 'next/headers'
import { logUserAction } from '@/app/utils'

export default async function Page() {
  // Read request data before scheduling `after`
  const userAgent = (await headers()).get('user-agent') || 'unknown'
  const sessionCookie =
    (await cookies()).get('session-id')?.value || 'anonymous'

  after(() => {
    logUserAction({ sessionCookie, userAgent })
  })

  return <h1>My Page</h1>
}
Calling cookies() or headers() inside the after callback in a Server Component will throw a runtime error.

With Cache Components

When using Cache Components, read request data in a dynamic (Suspense-wrapped) component and pass it into after:
app/page.tsx
import { Suspense } from 'react'
import { after } from 'next/server'
import { cookies } from 'next/headers'
import { logUserAction } from '@/app/utils'

export default function Page() {
  return (
    <>
      <h1>Part of the static shell</h1>
      <Suspense fallback={<p>Loading...</p>}>
        <DynamicContent />
      </Suspense>
    </>
  )
}

async function DynamicContent() {
  const sessionCookie =
    (await cookies()).get('session-id')?.value || 'anonymous'

  after(() => {
    logUserAction({ sessionCookie })
  })

  return <p>Your session: {sessionCookie}</p>
}

Platform support

Deployment optionSupported
Node.js serverYes
Docker containerYes
Static exportNo
AdaptersPlatform-specific
after depends on a waitUntil primitive. When self-hosting, see the self-hosting guide for configuration.

Version history

VersionChanges
v15.1.0after became stable.
v15.0.0-rcunstable_after introduced.

Build docs developers (and LLMs) love