Role-based access control
A logged-in user attempts to open an admin route without the admin role.
Next.js 16+ · Function
forbidden() stops a route segment with a 403 response when the current user is authenticated but lacks permission. Next.js renders the nearest forbidden UI and marks the response noindex.
Import forbidden from next/navigation and call it during the server render path when an authenticated user lacks permission. Next.js throws an internal 403 interruption, stops rendering that route segment, and displays the nearest forbidden.js or forbidden.tsx UI.
forbidden(): neverNever returns; it throws a 403 interruption that renders the forbidden UI.
import { forbidden } from 'next/navigation'
export default async function AdminPage() {
const user = await getUser()
if (user.role !== 'admin') {
forbidden()
}
return <h1>Admin area</h1>
}The page aborts rendering for non-admin users and displays the route's forbidden UI.
A logged-in user attempts to open an admin route without the admin role.
A user can access the application but lacks permission for a particular resource.
A server-side mutation detects insufficient permissions before completing the operation.
forbidden() is an authorization interruption imported from next/navigation. It throws a 403 error, terminates rendering of the current route segment, and renders the matching forbidden UI.
Follow-up: What happens to indexing? Next.js adds a noindex robots meta tag to the resulting page.
The route must be covered by a forbidden.js or forbidden.tsx file to customize its UI. That file is placed alongside the relevant route segment.
Follow-up: Can the root layout call forbidden()? No; the documentation explicitly excludes the root layout.
The API is experimental in the documented release and requires the experimental authInterrupts option in next.config.js. It can be used in Server Components, Server Functions, and Route Handlers.
Follow-up: What is the status code? It represents a 403 interruption, although response behavior can depend on whether headers have already been sent.
forbidden.jsunauthorizedauthInterruptsnotFound