Session tasks
Session tasks are pending requirements that users must complete after authentication, such as choosing an Organization. These tasks ensure that users meet all requirements before gaining full access to your application.
When enabled in the Clerk Dashboard, these tasks are handled automatically within the <SignIn /> and <SignUp /> components. If the prebuilt components don't meet your specific needs or if you require more control over the logic, you can opt out of using the <SignUp /> and <SignIn /> components and create a to display tasks.
This guide demonstrates how to use the Clerk API to build a custom user interface for handling session tasks.
Available tasks
Each task is identified by a unique SessionTask['key']. 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.
Detect pending session tasks
First, you need to tell your app where to redirect users when they have pending session tasks.
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.
Configure the taskUrls option on the <ClerkProvider> component.
<ClerkProvider
taskUrls={{
'choose-organization': '/session-tasks/choose-organization',
'reset-password': '/session-tasks/reset-password',
}}
>
{children}
</ClerkProvider>Configure the taskUrls option on the clerk() integration.
import { defineConfig } from 'astro/config'
import node from '@astrojs/node'
import clerk from '@clerk/astro'
export default defineConfig({
integrations: [
clerk({
taskUrls: {
'choose-organization': '/session-tasks/choose-organization',
'reset-password': '/session-tasks/reset-password',
},
}),
],
adapter: node({ mode: 'standalone' }),
output: 'server',
})Configure the taskUrls option on the clerk.load() method.
import { Clerk } from '@clerk/clerk-js'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey)
await clerk.load({
taskUrls: {
'choose-organization': '/session-tasks/choose-organization',
'reset-password': '/session-tasks/reset-password',
},
})Configure the taskUrls option on the clerkPlugin() integration.
import { createApp } from 'vue'
import './styles.css'
import App from './App.vue'
import { clerkPlugin } from '@clerk/vue'
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Add your Clerk publishable key to the .env.local file')
}
const app = createApp(App)
app.use(clerkPlugin, {
publishableKey: PUBLISHABLE_KEY,
taskUrls: {
'choose-organization': '/session-tasks/choose-organization',
'reset-password': '/session-tasks/reset-password',
},
})
app.mount('#app')Configure the taskUrls option on the defineNuxtConfig() integration.
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
modules: ['@clerk/nuxt'],
clerk: {
taskUrls: {
'choose-organization': '/session-tasks/choose-organization',
'reset-password': '/session-tasks/reset-password',
},
},
})Configure the taskUrls option on the clerkPlugin() integration.
import Fastify from 'fastify'
import { clerkPlugin } from '@clerk/fastify'
const fastify = Fastify({ logger: true })
fastify.register(clerkPlugin, {
taskUrls: {
'choose-organization': '/session-tasks/choose-organization',
'reset-password': '/session-tasks/reset-password',
},
})Display tasks
Now, the user will be redirected to the URL you've set with the taskUrls option. You need to display the appropriate task UI based on the task that the user needs to complete. If you were using the prebuilt components, you would simply render the appropriate component based on the task. However, since you're building a custom flow, you need to build the UI yourself. You can use the custom flow guide associated with the task to help get you started:
Protect routes
What if your user exits the authentication or session task flow before completing their tasks and doesn't know how to get to the appropriate page to complete their session tasks? What if your user is navigating through your app as a pending user and can't figure out why they can't access certain content?
If a user's authentication or session task flow is interrupted and they aren't able to complete the tasks, you can use the <RedirectToTasks /> component to redirect them to the appropriate task page so they can complete the tasks and move their session to an active (signed-in) state. This component will redirect users based on the URL's you've set with the taskUrls option.
In the following example, the <RedirectToTasks /> component is used to protect a page. Users can't access this page until they complete their pending session tasks. You can also wrap your entire application in the <RedirectToTasks /> component, or place it in your application's layout file, so that users can't access any of your app until they complete their pending session tasks.
In the following example, the <RedirectToTasks /> component is used in the app's layout file so that users can't access any of the app until they complete their pending session tasks. However, you can also use the <RedirectToTasks /> component to protect a single page or route group.
import { RedirectToTasks } from '@clerk/nextjs'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<RedirectToTasks />
{children}
</>
)
}import { RedirectToTasks } from '@clerk/clerk-react'
export default function Page() {
return <RedirectToTasks />
}import { RedirectToTasks } from '@clerk/react-router'
export default function Home() {
return <RedirectToTasks />
}import { RedirectToTasks } from '@clerk/chrome-extension'
export default function Home() {
return <RedirectToTasks />
}import { RedirectToTasks } from '@clerk/tanstack-react-start'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Home,
})
function Home() {
return <RedirectToTasks />
}<script setup lang="ts">
import { RedirectToTasks } from '@clerk/vue'
</script>
<template>
<RedirectToTasks />
</template><script setup lang="ts">
// Components are automatically imported
</script>
<template>
<RedirectToTasks />
</template>FAQ
What is the navigate parameter in setActive() doing?
In the authentication custom flows, such as the email/password custom flow, there's a step where you set the session to active using setActive(). If there are pending session tasks, the session won't actually be set as active. It will be in a pending state until all tasks are completed. By default, pending sessions are treated as signed-out across Clerk's authentication context, so the pending user won't be able to access protected content or routes. Therefore, this is the step where you should check for pending session tasks and redirect to the appropriate task page.
await setActive({
session: signInAttempt.createdSessionId,
navigate: async ({ session }) => {
// Check for tasks and navigate to custom UI to help users resolve them
// See https://clerk.com/docs/guides/development/custom-flows/authentication/session-tasks
if (session?.currentTask) {
console.log(session?.currentTask)
router.push(`/session-tasks/${session?.currentTask.key}`)
return
}
router.push('/')
},
})However, if you've set the taskUrls option on your Clerk integration, it will override the navigate behavior and will redirect the user to whatever URL path you've set for the task. It's recommended to rely on the taskUrls option so that you can maintain the task URLs in one place (your Clerk integration). However, the navigate parameter is still useful and can be used as a fallback.
Feedback
Last updated on
Edit on GitHub