Protect content from unauthenticated users - Access control - Next.js | Clerk Docs

Protect content from unauthenticated users

This guide demonstrates how to protect pages, Route Handlers, and Server Actions from unauthenticated users in your Next.js application.

To protect your application, every resource that reads or mutates protected data needs its own check. In a Next.js app, this means protecting:

Tip

To check whether a user is authorized (has a specific Role, Permission, Feature, or Plan)⁠ in addition to being signed in, see Authorization checks. This guide focuses on authentication (whether a user is signed in).

Tip

To help verify that protected Next.js resources keep their own auth checks over time, see the @clerk/eslint-plugin reference.

Server-side

auth() is an App Router-specific helper that you can use inside of your Route Handlers, Server Components, and Server Actions.

The following examples demonstrate both methods.

Protect a page

import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  // Redirects to the sign-in route if the user is not signed in
  await auth.protect()

return <h1>Hello world</h1>
}

Protect a layout

You can run an auth check in a layout the same way as in a page, and you should if the layout itself reads privileged data (for example, when prefetching). But a layout check alone isn't enough: layouts don't re-render on client-side navigation, so the session isn't re-checked on every route change. Protect each page (and every other resource that reads or mutates data) directly, and treat a layout check as an addition, not a replacement. For more information, see the Next.js docs.

Protect a Route Handler

For non-document requests, such as API requests, auth.protect() does not redirect the user to the sign-in page. Instead, it returns:

import { auth } from '@clerk/nextjs/server'

export const GET = async () => {
  // Returns a `404` error for unauthenticated requests
  const { userId } = await auth.protect()

return Response.json({ userId })
}

Protect a Server Action

Server Actions can be called from any Client Component, so verify the user inside the action itself, before running any logic that depends on them being signed in.

Inside a Server Action, auth.protect() doesn't redirect. Because a Server Action isn't a document request, it rejects unauthenticated requests with a 401 error instead. To handle the unauthenticated case yourself, use auth().

'use server'
import { auth } from '@clerk/nextjs/server'

export async function createPost(formData: FormData) {
  // Returns a 401 error if the user is not signed in
  const { userId } = await auth.protect()

// Add your Server Action logic using the `userId`
}

Client-side

On the client, you can read the isSignedIn property from the useAuth() or useUser() hook to conditionally render UI based on whether the user is signed in.

Warning

A client-side check is a UX affordance, not a security boundary. By the time this code runs, the component has already been sent to the browser, so anything it contains is available to the user regardless of the check.

Protect the data instead: guard the Route Handlers, Server Actions, and external API endpoints the client calls. Protecting the Server Components that render these Client Components also guards any data you pass down as props.

'use client'
import { useAuth } from '@clerk/nextjs'

export default function Page() {
  const { isLoaded, isSignedIn } = useAuth()

// Handle loading state
  if (!isLoaded) return <h1>Loading...</h1>

if (!isSignedIn) {
    // Add logic to handle the unauthenticated user
    // This example renders a UI but you could also redirect to the sign-in page instead
    return <h1>You must be signed in to view this page</h1>
  }

return <h1>Hello world</h1>
}