Skip to main content
Docs

Session tasks

Tasks are pending requirements that users must complete after authentication, such as choosing an organization. When enabled in the Clerk Dashboard, these tasks are handled automatically within the <SignIn /> and <SignUp /> components.

Available tasks

Each task is identified by a unique . You can use these task keys to conditionally handle different requirements in your application logic.

The following table lists the available tasks and their corresponding keys.

SettingKeyDescription
Allow personal accountschoose-organizationDisabled by default when enabling organizations, requires users to choose an organization after authenticating. When disabled, users can choose a personal account.

Session states

After authentication, users enter one of three states:

  • Signed-in: Authentication complete. Can access protected content or routes.
  • Pending: Authentication complete, but session tasks incomplete. Can't access protected content or routes.
  • Signed-out: Authentication failed or not attempted. Can't access protected content or routes.

When authenticating, sessions remain pending until users complete the required tasks. By default, pending sessions are treated as signed-out across Clerk's authentication context.

Implementation

Displaying tasks

Once enabled in the Clerk Dashboard, tasks are embedded by default within the <SignUp /> and <SignIn /> components. For more customization, you can opt out of using the <SignUp /> and <SignIn /> components and create custom pages for tasks.

The following table lists the available tasks and their corresponding components. See the linked reference guide for usage instructions.

Important

Personal accounts being disabled by default was released on 08-22-2025. Applications created before this date will not be able to see the Allow personal accounts setting, because personal account were enabled by default.

If the prebuilt components don't meet your specific needs or if you require more control over the logic, you can also build your own UI using the Session.currentTask property to check if the user has pending session tasks. Refer to the custom flows documentation for specific implementation guides.

import { useSession } from '@clerk/clerk-react'

const { session } = useSession()

if (session?.currentTask) {
  // Check for pending tasks and display custom UI to help users resolve them
  // See https://clerk.com/docs/custom-flows/overview#session-tasks
  console.log(session?.currentTask)
}
if (window.Clerk.session.currentTask) {
  // Check for pending tasks and display custom UI to help users resolve them
  // See https://clerk.com/docs/custom-flows/overview#session-tasks
  console.log(window.Clerk.session.currentTask)
}

Redirecting to tasks

When users have pending session tasks, you can redirect them to specific pages or components to complete these requirements. This is useful when you want to handle session tasks outside of the default <SignIn /> and <SignUp /> components.

Using the taskUrls option

The taskUrls option allows you to specify custom URL paths where users are redirected after sign-up or sign-in when specific session tasks need to be completed. This allows you to still use <SignIn /> and <SignUp /> but have tasks with custom pages.

Configure the taskUrls option in your root layout to specify where users should be redirected for different session tasks.

app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider
      publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
      taskUrls={{
        'choose-organization': '/onboarding/choose-organization',
      }}
    >
      {children}
    </ClerkProvider>
  )
}

Create a custom page that imports the TaskChooseOrganization component to handle the task.

app/onboarding/choose-organization/page.tsx
import { TaskChooseOrganization } from '@clerk/nextjs'

export default function ChooseOrganizationPage() {
  return <TaskChooseOrganization redirectUrlComplete="/dashboard" />
}

Using the RedirectToTasks control component

The <RedirectToTasks /> control component redirects users to the appropriate task page when they have pending session tasks.

app/dashboard/layout.tsx
import { SignedOut, RedirectToTasks } from '@clerk/nextjs'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <SignedOut>
        <RedirectToTasks />
      </SignedOut>
      {children}
    </>
  )
}

Middleware-based redirects

For auth.protect(), signed-out users will be redirected to the sign-in page. In the following example, pending users will be redirected to the sign-in page, where the <SignIn /> component will prompt them to fulfill the session tasks, which in this case is selecting or creating an organization. Once finished, their session will move from pending to an active (signed-in) state.

middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)'])

export default clerkMiddleware(async (auth, req) => {
  // pending users won't be able to access protected routes
  // and will be redirected to the sign-in page
  if (isProtectedRoute(req)) await auth.protect()
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

For auth().userId, it would return null if the user has a pending session. Pending users will be redirected to the sign-in page, where the <SignIn /> component will prompt them to fulfill the session tasks. Once finished, their session will move from pending to a signed-in state.

app/middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)'])

export default clerkMiddleware(async (auth, req) => {
  const { userId, redirectToSignIn } = await auth()

  // pending users will not have a `userId`
  // and will be redirected to the sign-in page
  if (!userId && isProtectedRoute(req)) {
    return redirectToSignIn()
  }
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

Session handling

You can check the current authentication state using the Clerk.isSignedIn property. By default, pending sessions are treated as signed-out across Clerk's authentication context. Some control components and authentication utilities accept a treatPendingAsSignedOut prop to control how pending sessions are handled:

  • true (default): Treats pending sessions as signed-out. Users can't access protected content or routes.
  • false: Treats pending sessions as signed-in. Users can access protected content or routes.

Control components

The <Protect /> component protects content or even entire routes based on a user's authentication state. It will render its children if the user's state is signed-in. It accepts a fallback prop that will be rendered if the user's state is signed-out.

If the user's state is pending, they will see the fallback content because, by default, pending sessions are treated as signed-out.

export default function Page() {
  return (
    <Protect fallback={<p>Signed-out and pending users can see this.</p>}>
      <p>Only signed-in users can see this.</p>
    </Protect>
  )
}

If the user's state is pending, they will see the protected content because treatPendingAsSignedOut is set to false.

export default function Page() {
  return (
    <Protect
      treatPendingAsSignedOut={false}
      fallback={<p>Users that are signed-out can see this.</p>}
    >
      <p>Users that are signed-in or pending can see this.</p>
    </Protect>
  )
}

The <SignedOut /> component renders its children if the user's authentication state is signed-out.

If the user's state is pending, they will see the content of the component because, by default, pending sessions are treated as signed-out.

export default function Page() {
  return (
    <SignedOut>
      <p>Users that are signed-out or pending will see this.</p>
    </SignedOut>
  )
}

If the user's state is pending, they won't see the content of the component because treatPendingAsSignedOut is set to false.

export default function Page() {
  return (
    <SignedOut treatPendingAsSignedOut={false}>
      <p>Users that are signed-out will see this.</p>
    </SignedOut>
  )
}

The <SignedIn /> component renders its children if the user's authentication state is signed-in.

If the user's state is pending, they won't see the content of the component because, by default, pending sessions are treated as signed-out.

export default function Page() {
  return (
    <SignedIn>
      <p>Users that are signed-in will see this.</p>
    </SignedIn>
  )
}

If the user's state is pending, they will see the content of the component because treatPendingAsSignedOut is set to false.

export default function Page() {
  return (
    <SignedIn treatPendingAsSignedOut={false}>
      <p>Users that are signed-in or pending will see this.</p>
    </SignedIn>
  )
}

Authentication utilities

The useAuth() hook and helpers that access the , such as getAuth() or request.auth, will return null if the user has a pending session. Most utilities accept a treatPendingAsSignedOut option that defaults to true. You can pass false to treat pending sessions as signed-in.

Example: Personal accounts disabled

When organizations are enabled, personal accounts are disabled by default and your users will be required to select or create an organization after authenticating. Until completed, their session remains pending. Pages that are protected using Clerk's protection utilities will treat the user's session as signed-out.

For useAuth(), userId will be null if the user has a pending session.

export default function Dashboard() {
  const { isSignedIn, userId } = useAuth()

  if (!userId) {
    return (
      <p>
        User has no session or a pending session. They either need to sign in, or they need to
        complete tasks by selecting or creating an organization.
      </p>
    )
  }

  return (
    <p>
      <p>User has a valid session and {orgId} is defined</p>
    </p>
  )
}

For auth().userId, it would return null if the user has a pending session.

app/page.tsx
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { userId, orgId } = await auth()

  if (!userId) {
    return (
      <p>
        User has no session or a pending session. They either need to sign in, or they need to
        complete pending session tasks by selecting or creating an organization.
      </p>
    )
  }

  return (
    <p>
      <p>User has a valid session and {orgId} is defined</p>
    </p>
  )
}

By default, users with a pending session are treated as signed-out, and their userId will not be available. However, in some cases, you may want to access the user's ID even if their session is still pending. In these cases, you can set treatPendingAsSignedOut to false, which will treat pending sessions as signed-in and allow you to access the userId.

app/api/get-teams/route.tsx
import { auth } from '@clerk/nextjs/server'

export const POST = async () => {
  // `treatPendingAsSignedOut` is set to `false` to allow access to the `userId` for pending sessions
  const { userId, has } = await auth({ treatPendingAsSignedOut: false })

  // Check if the user is signed-out
  if (!userId) {
    return Response.json({ error: 'User is signed-out' }, { status: 401 })
  }

  // Now the pending user's `userId` can be used for your use case
  // This is a basic example of creating a resource using the `userId`
  try {
    const newResource = await resources.create({
      userId,
    })

    return Response.json({ data: newResource }, { status: 201 })
  } catch (error) {
    return Response.json({ error: 'Failed to create resource' }, { status: 500 })
  }
}

Feedback

What did you think of this content?

Last updated on