From 780cae54244b84b8f0e3d46c3739bf0d5cc4c99d Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Tue, 30 Sep 2025 15:21:55 -0600 Subject: [PATCH 01/18] Initial setup --- packages/react/docs/use-auth.md | 43 --------------------- packages/react/docs/use-sign-in.md | 20 ---------- packages/react/src/hooks/useAuth.ts | 54 --------------------------- packages/react/src/hooks/useSignIn.ts | 38 ------------------- 4 files changed, 155 deletions(-) delete mode 100644 packages/react/docs/use-auth.md delete mode 100644 packages/react/docs/use-sign-in.md diff --git a/packages/react/docs/use-auth.md b/packages/react/docs/use-auth.md deleted file mode 100644 index 18ed8424cd7..00000000000 --- a/packages/react/docs/use-auth.md +++ /dev/null @@ -1,43 +0,0 @@ - - -```tsx {{ filename: 'app/external-data/page.tsx' }} -'use client'; - -import { useAuth } from '@clerk/nextjs'; - -export default function ExternalDataPage() { - const { userId, sessionId, getToken, isLoaded, isSignedIn } = useAuth(); - - const fetchExternalData = async () => { - const token = await getToken(); - - // Fetch data from an external API - const response = await fetch('https://api.example.com/data', { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - return response.json(); - }; - - if (!isLoaded) { - return
Loading...
; - } - - if (!isSignedIn) { - return
Sign in to view this page
; - } - - return ( -
-

- Hello, {userId}! Your current active session is {sessionId}. -

- -
- ); -} -``` - - diff --git a/packages/react/docs/use-sign-in.md b/packages/react/docs/use-sign-in.md deleted file mode 100644 index 55100d7e212..00000000000 --- a/packages/react/docs/use-sign-in.md +++ /dev/null @@ -1,20 +0,0 @@ - - -```tsx {{ filename: 'app/sign-in/page.tsx' }} -'use client'; - -import { useSignIn } from '@clerk/nextjs'; - -export default function SignInPage() { - const { isLoaded, signIn } = useSignIn(); - - if (!isLoaded) { - // Handle loading state - return null; - } - - return
The current sign-in attempt status is {signIn?.status}.
; -} -``` - - diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index e5df2986156..e1865a077d7 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -37,60 +37,6 @@ type UseAuthOptions = Record | PendingSessionOptions | undefined | * * @param [initialAuthStateOrOptions] - An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. * - * @function - * - * @example - * - * The following example demonstrates how to use the `useAuth()` hook to access the current auth state, like whether the user is signed in or not. It also includes a basic example for using the `getToken()` method to retrieve a session token for fetching data from an external resource. - * - * - * - * - * ```tsx {{ filename: 'src/pages/ExternalDataPage.tsx' }} - * import { useAuth } from '@clerk/clerk-react' - * - * export default function ExternalDataPage() { - * const { userId, sessionId, getToken, isLoaded, isSignedIn } = useAuth() - * - * const fetchExternalData = async () => { - * const token = await getToken() - * - * // Fetch data from an external API - * const response = await fetch('https://api.example.com/data', { - * headers: { - * Authorization: `Bearer ${token}`, - * }, - * }) - * - * return response.json() - * } - * - * if (!isLoaded) { - * return
Loading...
- * } - * - * if (!isSignedIn) { - * return
Sign in to view this page
- * } - * - * return ( - *
- *

- * Hello, {userId}! Your current active session is {sessionId}. - *

- * - *
- * ) - * } - * ``` - * - *
- * - * - * {@include ../../docs/use-auth.md#nextjs-01} - * - * - *
*/ export const useAuth = (initialAuthStateOrOptions: UseAuthOptions = {}): UseAuthReturn => { useAssertWrappedByClerkProvider('useAuth'); diff --git a/packages/react/src/hooks/useSignIn.ts b/packages/react/src/hooks/useSignIn.ts index 2b7fb2e8082..a6c7e2b138e 100644 --- a/packages/react/src/hooks/useSignIn.ts +++ b/packages/react/src/hooks/useSignIn.ts @@ -10,44 +10,6 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid * * @unionReturnHeadings * ["Initialization", "Loaded"] - * - * @example - * ### Check the current state of a sign-in - * - * The following example uses the `useSignIn()` hook to access the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which contains the current sign-in attempt status and methods to create a new sign-in attempt. The `isLoaded` property is used to handle the loading state. - * - * - * - * - * ```tsx {{ filename: 'src/pages/SignInPage.tsx' }} - * import { useSignIn } from '@clerk/clerk-react' - * - * export default function SignInPage() { - * const { isLoaded, signIn } = useSignIn() - * - * if (!isLoaded) { - * // Handle loading state - * return null - * } - * - * return
The current sign-in attempt status is {signIn?.status}.
- * } - * ``` - * - *
- * - * - * {@include ../../docs/use-sign-in.md#nextjs-01} - * - * - *
- * - * @example - * ### Create a custom sign-in flow with `useSignIn()` - * - * The `useSignIn()` hook can also be used to build fully custom sign-in flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-in flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignIn()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/guides/development/custom-flows/overview). - * - * ```empty``` */ export const useSignIn = (): UseSignInReturn => { useAssertWrappedByClerkProvider('useSignIn'); From a584905ca549e19b8158710b1071e01cb66eddb0 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 1 Oct 2025 12:31:46 -0600 Subject: [PATCH 02/18] Test --- packages/react/src/hooks/useAuth.ts | 51 +++++++++++++++---- .../src/react/hooks/useReverification.ts | 20 +++++--- packages/shared/src/react/hooks/useSession.ts | 21 -------- packages/types/src/hooks.ts | 6 ++- 4 files changed, 59 insertions(+), 39 deletions(-) diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index e1865a077d7..659b084097f 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -18,25 +18,56 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid import { createGetToken, createSignOut } from './utils'; /** - * @inline + * @interface + */ +export type UseAuthOptions = Record | PendingSessionOptions | undefined | null; +/** + * + * @param initialAuthStateOrOptions - An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. */ -type UseAuthOptions = Record | PendingSessionOptions | undefined | null; /** * The `useAuth()` hook provides access to the current user's authentication state and methods to manage the active session. * - * > [!NOTE] - * > To access auth data server-side, see the [`Auth` object reference doc](https://clerk.com/docs/reference/backend/types/auth-object). + * The following example demonstrates how to use the `useAuth()` hook to access the current auth state, like whether the user is signed in or not. It also includes a basic example for using the `getToken()` method to retrieve a session token for fetching data from an external resource. + * + * ```tsx {{ filename: 'src/pages/ExternalDataPage.tsx' }} + * import { useAuth } from '@clerk/clerk-react' * - * - * By default, Next.js opts all routes into static rendering. If you need to opt a route or routes into dynamic rendering because you need to access the authentication data at request time, you can create a boundary by passing the `dynamic` prop to ``. See the [guide on rendering modes](https://clerk.com/docs/guides/development/rendering-modes) for more information, including code examples. - * + * export default function ExternalDataPage() { + * const { userId, sessionId, getToken, isLoaded, isSignedIn } = useAuth() * - * @unionReturnHeadings - * ["Initialization", "Signed out", "Signed in (no active organization)", "Signed in (with active organization)"] + * const fetchExternalData = async () => { + * const token = await getToken() * - * @param [initialAuthStateOrOptions] - An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. + * // Fetch data from an external API + * const response = await fetch('https://api.example.com/data', { + * headers: { + * Authorization: `Bearer ${token}`, + * }, + * }) * + * return response.json() + * } + * + * if (!isLoaded) { + * return
Loading...
+ * } + * + * if (!isSignedIn) { + * return
Sign in to view this page
+ * } + * + * return ( + *
+ *

+ * Hello, {userId}! Your current active session is {sessionId}. + *

+ * + *
+ * ) + * } + * ``` */ export const useAuth = (initialAuthStateOrOptions: UseAuthOptions = {}): UseAuthReturn => { useAssertWrappedByClerkProvider('useAuth'); diff --git a/packages/shared/src/react/hooks/useReverification.ts b/packages/shared/src/react/hooks/useReverification.ts index dadbe438104..2367351c11b 100644 --- a/packages/shared/src/react/hooks/useReverification.ts +++ b/packages/shared/src/react/hooks/useReverification.ts @@ -11,6 +11,9 @@ import { useSafeLayoutEffect } from './useSafeLayoutEffect'; const CLERK_API_REVERIFICATION_ERROR_CODE = 'session_reverification_required'; +/** + * + */ async function resolveResult(result: Promise | T): Promise> { try { const r = await result; @@ -34,7 +37,7 @@ type ExcludeClerkError = T extends { clerk_error: any } ? never : T; /** * @interface */ -type NeedsReverificationParameters = { +export type NeedsReverificationParameters = { cancel: () => void; complete: () => void; level: SessionVerificationLevel | undefined; @@ -42,16 +45,16 @@ type NeedsReverificationParameters = { /** * The optional options object. + * * @interface */ -type UseReverificationOptions = { +export type UseReverificationOptions = { /** * A handler that is called when reverification is needed, this will opt-out of using the default UI when provided. * * @param cancel - A function that will cancel the reverification process. * @param complete - A function that will retry the original request after reverification. * @param level - The level returned with the reverification hint. - * */ onNeedsReverification?: (properties: NeedsReverificationParameters) => void; }; @@ -59,14 +62,14 @@ type UseReverificationOptions = { /** * @interface */ -type UseReverificationResult Promise | undefined> = ( +export type UseReverificationResult Promise | undefined> = ( ...args: Parameters ) => Promise>>>; /** * @interface */ -type UseReverification = < +export type UseReverification = < Fetcher extends (...args: any[]) => Promise | undefined, Options extends UseReverificationOptions = UseReverificationOptions, >( @@ -79,7 +82,13 @@ type CreateReverificationHandlerParams = UseReverificationOptions & { telemetry: Clerk['telemetry']; }; +/** + * + */ function createReverificationHandler(params: CreateReverificationHandlerParams) { + /** + * + */ function assertReverification Promise | undefined>( fetcher: Fetcher, ): (...args: Parameters) => Promise>>> { @@ -191,7 +200,6 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) * return * } * ``` - * */ export const useReverification: UseReverification = (fetcher, options) => { const { __internal_openReverification, telemetry } = useClerk(); diff --git a/packages/shared/src/react/hooks/useSession.ts b/packages/shared/src/react/hooks/useSession.ts index 7427e53baf3..6f287ee37fa 100644 --- a/packages/shared/src/react/hooks/useSession.ts +++ b/packages/shared/src/react/hooks/useSession.ts @@ -9,21 +9,8 @@ const hookName = `useSession`; /** * The `useSession()` hook provides access to the current user's [`Session`](https://clerk.com/docs/reference/javascript/session) object, as well as helpers for setting the active session. * - * @unionReturnHeadings - * ["Initialization", "Signed out", "Signed in"] - * - * @function - * - * @param [options] - An object containing options for the `useSession()` hook. - * - * @example - * ### Access the `Session` object - * * The following example uses the `useSession()` hook to access the `Session` object, which has the `lastActiveAt` property. The `lastActiveAt` property is a `Date` object used to show the time the session was last active. * - * - * - * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useSession } from '@clerk/clerk-react' * @@ -46,14 +33,6 @@ const hookName = `useSession`; * ) * } * ``` - * - * - * - * - * {@include ../../../docs/use-session.md#nextjs-01} - * - * - * */ export const useSession: UseSession = () => { useAssertWrappedByClerkProvider(hookName); diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 6c814689fe3..943483fe9f1 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -21,7 +21,8 @@ type CheckAuthorizationSignedOut = undefined; type CheckAuthorizationWithoutOrgOrUser = (params: Parameters[0]) => false; /** - * @inline + * @unionReturnHeadings + * ["Initialization", "Signed out", "Signed in (no active organization)", "Signed in (with active organization)"] */ export type UseAuthReturn = | { @@ -166,7 +167,8 @@ export type UseSignUpReturn = }; /** - * @inline + * @unionReturnHeadings + * ["Initialization", "Signed out", "Signed in"] */ export type UseSessionReturn = | { From 8d2e9e2631fc329c5c75e36bbedfcfa9f23e9e55 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 1 Oct 2025 15:31:01 -0600 Subject: [PATCH 03/18] Refactor typedoc --- packages/react/docs/use-sign-up.md | 20 ----- packages/react/src/hooks/useSignIn.ts | 2 - packages/react/src/hooks/useSignUp.ts | 17 ---- packages/react/typedoc.json | 2 +- packages/shared/docs/use-clerk.md | 15 ---- packages/shared/docs/use-session-list.md | 24 ------ packages/shared/docs/use-session.md | 28 ------- packages/shared/docs/use-user.md | 81 ------------------- packages/shared/src/react/hooks/useClerk.ts | 21 +---- .../shared/src/react/hooks/useSessionList.ts | 16 ---- .../src/react/hooks/useSubscription.tsx | 55 ++++++++++++- packages/shared/src/react/hooks/useUser.ts | 28 ------- packages/types/src/hooks.ts | 12 ++- 13 files changed, 63 insertions(+), 258 deletions(-) delete mode 100644 packages/react/docs/use-sign-up.md delete mode 100644 packages/shared/docs/use-clerk.md delete mode 100644 packages/shared/docs/use-session-list.md delete mode 100644 packages/shared/docs/use-session.md delete mode 100644 packages/shared/docs/use-user.md diff --git a/packages/react/docs/use-sign-up.md b/packages/react/docs/use-sign-up.md deleted file mode 100644 index 53d1cb10289..00000000000 --- a/packages/react/docs/use-sign-up.md +++ /dev/null @@ -1,20 +0,0 @@ - - -```tsx {{ filename: 'app/sign-up/page.tsx' }} -'use client'; - -import { useSignUp } from '@clerk/nextjs'; - -export default function SignUpPage() { - const { isLoaded, signUp } = useSignUp(); - - if (!isLoaded) { - // Handle loading state - return null; - } - - return
The current sign-up attempt status is {signUp?.status}.
; -} -``` - - diff --git a/packages/react/src/hooks/useSignIn.ts b/packages/react/src/hooks/useSignIn.ts index a6c7e2b138e..308326a568d 100644 --- a/packages/react/src/hooks/useSignIn.ts +++ b/packages/react/src/hooks/useSignIn.ts @@ -8,8 +8,6 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid /** * The `useSignIn()` hook provides access to the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which allows you to check the current state of a sign-in attempt and manage the sign-in flow. You can use this to create a [custom sign-in flow](https://clerk.com/docs/guides/development/custom-flows/overview#sign-in-flow). * - * @unionReturnHeadings - * ["Initialization", "Loaded"] */ export const useSignIn = (): UseSignInReturn => { useAssertWrappedByClerkProvider('useSignIn'); diff --git a/packages/react/src/hooks/useSignUp.ts b/packages/react/src/hooks/useSignUp.ts index 5b3af8e7804..c6ffb8fbba7 100644 --- a/packages/react/src/hooks/useSignUp.ts +++ b/packages/react/src/hooks/useSignUp.ts @@ -8,17 +8,10 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid /** * The `useSignUp()` hook provides access to the [`SignUp`](https://clerk.com/docs/reference/javascript/sign-up) object, which allows you to check the current state of a sign-up attempt and manage the sign-up flow. You can use this to create a [custom sign-up flow](https://clerk.com/docs/guides/development/custom-flows/overview#sign-up-flow). * - * @unionReturnHeadings - * ["Initialization", "Loaded"] - * - * @example * ### Check the current state of a sign-up * * The following example uses the `useSignUp()` hook to access the [`SignUp`](https://clerk.com/docs/reference/javascript/sign-up) object, which contains the current sign-up attempt status and methods to create a new sign-up attempt. The `isLoaded` property is used to handle the loading state. * - * - * - * * ```tsx {{ filename: 'src/pages/SignUpPage.tsx' }} * import { useSignUp } from '@clerk/clerk-react' * @@ -34,20 +27,10 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid * } * ``` * - * - * - * - * {@include ../../docs/use-sign-up.md#nextjs-01} - * - * - * - * - * @example * ### Create a custom sign-up flow with `useSignUp()` * * The `useSignUp()` hook can also be used to build fully custom sign-up flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-up flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignUp()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/guides/development/custom-flows/overview). * - * ```empty``` */ export const useSignUp = (): UseSignUpReturn => { useAssertWrappedByClerkProvider('useSignUp'); diff --git a/packages/react/typedoc.json b/packages/react/typedoc.json index 2d417a9b83f..4eab6484506 100644 --- a/packages/react/typedoc.json +++ b/packages/react/typedoc.json @@ -1,4 +1,4 @@ { "$schema": "https://typedoc.org/schema.json", - "entryPoints": ["./src/index.ts", "./src/experimental.ts"] + "entryPoints": ["./src/index.ts", "./src/experimental.ts", "./src/hooks/*.{ts,tsx}"] } diff --git a/packages/shared/docs/use-clerk.md b/packages/shared/docs/use-clerk.md deleted file mode 100644 index 839672b69cf..00000000000 --- a/packages/shared/docs/use-clerk.md +++ /dev/null @@ -1,15 +0,0 @@ - - -```tsx {{ filename: 'app/page.tsx' }} -'use client'; - -import { useClerk } from '@clerk/nextjs'; - -export default function HomePage() { - const clerk = useClerk(); - - return ; -} -``` - - diff --git a/packages/shared/docs/use-session-list.md b/packages/shared/docs/use-session-list.md deleted file mode 100644 index c4441a59b95..00000000000 --- a/packages/shared/docs/use-session-list.md +++ /dev/null @@ -1,24 +0,0 @@ - - -```tsx {{ filename: 'app/page.tsx' }} -'use client'; - -import { useSessionList } from '@clerk/nextjs'; - -export default function HomePage() { - const { isLoaded, sessions } = useSessionList(); - - if (!isLoaded) { - // Handle loading state - return null; - } - - return ( -
-

Welcome back. You've been here {sessions.length} times before.

-
- ); -} -``` - - diff --git a/packages/shared/docs/use-session.md b/packages/shared/docs/use-session.md deleted file mode 100644 index 95be5884665..00000000000 --- a/packages/shared/docs/use-session.md +++ /dev/null @@ -1,28 +0,0 @@ - - -```tsx {{ filename: 'app/page.tsx' }} -'use client'; - -import { useSession } from '@clerk/nextjs'; - -export default function HomePage() { - const { isLoaded, session, isSignedIn } = useSession(); - - if (!isLoaded) { - // Handle loading state - return null; - } - if (!isSignedIn) { - // Handle signed out state - return null; - } - - return ( -
-

This session has been active since {session.lastActiveAt.toLocaleString()}

-
- ); -} -``` - - diff --git a/packages/shared/docs/use-user.md b/packages/shared/docs/use-user.md deleted file mode 100644 index 106804ac014..00000000000 --- a/packages/shared/docs/use-user.md +++ /dev/null @@ -1,81 +0,0 @@ - - -```tsx {{ filename: 'app/page.tsx' }} -'use client'; - -import { useUser } from '@clerk/nextjs'; - -export default function HomePage() { - const { isSignedIn, isLoaded, user } = useUser(); - - if (!isLoaded) { - // Handle loading state - return null; - } - - if (!isSignedIn) return null; - - const updateUser = async () => { - await user.update({ - firstName: 'John', - lastName: 'Doe', - }); - }; - - return ( - <> - -

user.firstName: {user.firstName}

-

user.lastName: {user.lastName}

- - ); -} -``` - - - - - -```tsx {{ filename: 'app/page.tsx' }} -'use client'; - -import { useUser } from '@clerk/nextjs'; - -export default function HomePage() { - const { isSignedIn, isLoaded, user } = useUser(); - - if (!isLoaded) { - // Handle loading state - return null; - } - - if (!isSignedIn) return null; - - const updateUser = async () => { - // Update data via an API endpoint - const updateMetadata = await fetch('/api/updateMetadata', { - method: 'POST', - body: JSON.stringify({ - role: 'admin', - }), - }); - - // Check if the update was successful - if ((await updateMetadata.json()).message !== 'success') { - throw new Error('Error updating'); - } - - // If the update was successful, reload the user data - await user.reload(); - }; - - return ( - <> - -

user role: {user.publicMetadata.role}

- - ); -} -``` - - diff --git a/packages/shared/src/react/hooks/useClerk.ts b/packages/shared/src/react/hooks/useClerk.ts index 87f36ca3c2c..1f763d97d61 100644 --- a/packages/shared/src/react/hooks/useClerk.ts +++ b/packages/shared/src/react/hooks/useClerk.ts @@ -8,34 +8,17 @@ import { useAssertWrappedByClerkProvider, useClerkInstanceContext } from '../con * * The `useClerk()` hook provides access to the [`Clerk`](https://clerk.com/docs/reference/javascript/clerk) object, allowing you to build alternatives to any Clerk Component. * - * @function - * - * @returns The `useClerk()` hook returns the `Clerk` object, which includes all the methods and properties listed in the [`Clerk` reference](https://clerk.com/docs/reference/javascript/clerk). - * - * @example - * * The following example uses the `useClerk()` hook to access the `clerk` object. The `clerk` object is used to call the [`openSignIn()`](https://clerk.com/docs/reference/javascript/clerk#sign-in) method to open the sign-in modal. * - * - * - * * ```tsx {{ filename: 'src/Home.tsx' }} - * import { useClerk } from '@clerk/clerk-react' + * import { useClerk } from '@clerk/clerk-react'. * * export default function Home() { - * const clerk = useClerk() + * const clerk = useClerk(). * * return * } * ``` - * - * - * - * - * {@include ../../../docs/use-clerk.md#nextjs-01} - * - * - * */ export const useClerk = (): LoadedClerk => { useAssertWrappedByClerkProvider('useClerk'); diff --git a/packages/shared/src/react/hooks/useSessionList.ts b/packages/shared/src/react/hooks/useSessionList.ts index 6d0ee1b0bd1..eb6a357fdf3 100644 --- a/packages/shared/src/react/hooks/useSessionList.ts +++ b/packages/shared/src/react/hooks/useSessionList.ts @@ -7,19 +7,10 @@ const hookName = 'useSessionList'; /** * The `useSessionList()` hook returns an array of [`Session`](https://clerk.com/docs/reference/javascript/session) objects that have been registered on the client device. * - * @unionReturnHeadings - * ["Initialization", "Loaded"] - * - * @function - * - * @example * ### Get a list of sessions * * The following example uses `useSessionList()` to get a list of sessions that have been registered on the client device. The `sessions` property is used to show the number of times the user has visited the page. * - * - * - * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useSessionList } from '@clerk/clerk-react' * @@ -39,13 +30,6 @@ const hookName = 'useSessionList'; * } * ``` * - * - * - * - * {@include ../../../docs/use-session-list.md#nextjs-01} - * - * - * */ export const useSessionList = (): UseSessionListReturn => { useAssertWrappedByClerkProvider(hookName); diff --git a/packages/shared/src/react/hooks/useSubscription.tsx b/packages/shared/src/react/hooks/useSubscription.tsx index 53c48df8c08..a2197ebf0f5 100644 --- a/packages/shared/src/react/hooks/useSubscription.tsx +++ b/packages/shared/src/react/hooks/useSubscription.tsx @@ -1,4 +1,4 @@ -import type { EnvironmentResource, ForPayerType } from '@clerk/types'; +import type { BillingSubscriptionResource, EnvironmentResource, ForPayerType } from '@clerk/types'; import { useCallback } from 'react'; import { eventMethodCalled } from '../../telemetry/events'; @@ -12,16 +12,65 @@ import { const hookName = 'useSubscription'; -type UseSubscriptionParams = { +/** + * @interface + */ +export type UseSubscriptionParams = { + /** + * Specifies whether to fetch subscription for an organization or user. + * + * @default 'user' + */ for?: ForPayerType; /** - * If `true`, the previous data will be kept in the cache until new data is fetched. + * If `true`, the previous data will be kept in the cache until new data is fetched. This helps prevent layout shifts. * * @default false */ keepPreviousData?: boolean; }; +/** + * @interface + */ +export type UseSubscriptionReturn = + | { + /** + * A boolean that indicates whether the initial data is still being fetched. + */ + data: null; + /** + * A boolean that indicates whether the initial data is still being fetched. + */ + isLoaded: false; + /** + * A boolean that indicates whether any request is still in flight, including background updates. + */ + isFetching: false; + /** + * Any error that occurred during the data fetch, or `null` if no error occurred. + */ + error: null; + /** + * Function to manually trigger a refresh of the subscription data. + */ + revalidate: () => Promise; + } + | { + data: BillingSubscriptionResource; + isLoaded: true; + isFetching: true; + error: Error; + revalidate: () => Promise; + } + | { + data: BillingSubscriptionResource | null; + isLoaded: boolean; + isFetching: boolean; + error: Error | null; + revalidate: () => Promise; + }; + /** * @internal * diff --git a/packages/shared/src/react/hooks/useUser.ts b/packages/shared/src/react/hooks/useUser.ts index a25367b8c89..74fb27f056a 100644 --- a/packages/shared/src/react/hooks/useUser.ts +++ b/packages/shared/src/react/hooks/useUser.ts @@ -7,12 +7,6 @@ const hookName = 'useUser'; /** * The `useUser()` hook provides access to the current user's [`User`](https://clerk.com/docs/reference/javascript/user) object, which contains all the data for a single user in your application and provides methods to manage their account. This hook also allows you to check if the user is signed in and if Clerk has loaded and initialized. * - * @unionReturnHeadings - * ["Initialization", "Signed out", "Signed in"] - * - * @example - * ### Get the current user - * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which contains the current user's data such as their full name. The `isLoaded` and `isSignedIn` properties are used to handle the loading state and to check if the user is signed in, respectively. * * ```tsx {{ filename: 'src/Example.tsx' }} @@ -33,14 +27,10 @@ const hookName = 'useUser'; * } * ``` * - * @example * ### Update user data * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which calls the [`update()`](https://clerk.com/docs/reference/javascript/user#update) method to update the current user's information. * - * - * - * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useUser } from '@clerk/clerk-react' * @@ -70,22 +60,11 @@ const hookName = 'useUser'; * ) * } * ``` - * - * - * - * {@include ../../../docs/use-user.md#nextjs-01} * - * - * - * - * @example * ### Reload user data * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which calls the [`reload()`](https://clerk.com/docs/reference/javascript/user#reload) method to get the latest user's information. * - * - * - * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useUser } from '@clerk/clerk-react' * @@ -126,13 +105,6 @@ const hookName = 'useUser'; * } * ``` * - * - * - * - * {@include ../../../docs/use-user.md#nextjs-02} - * - * - * */ export function useUser(): UseUserReturn { useAssertWrappedByClerkProvider(hookName); diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 943483fe9f1..e710e059e54 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -119,7 +119,8 @@ export type UseAuthReturn = }; /** - * @inline + * @unionReturnHeadings + * ["Initialization", "Loaded"] */ export type UseSignInReturn = | { @@ -143,7 +144,8 @@ export type UseSignInReturn = }; /** - * @inline + * @unionReturnHeadings + * ["Initialization", "Loaded"] */ export type UseSignUpReturn = | { @@ -197,7 +199,8 @@ export type UseSessionReturn = }; /** - * @inline + * @unionReturnHeadings + * ["Initialization", "Loaded"] */ export type UseSessionListReturn = | { @@ -221,7 +224,8 @@ export type UseSessionListReturn = }; /** - * @inline + * @unionReturnHeadings + * ["Initialization", "Signed out", "Signed in"] */ export type UseUserReturn = | { From 70c5908d34b067f8d9af70a169f7d80c8ba25abd Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 1 Oct 2025 15:40:03 -0600 Subject: [PATCH 04/18] Bring back removed info --- packages/react/src/hooks/useSignIn.ts | 23 +++++++++++++++++++ packages/shared/src/react/hooks/useClerk.ts | 4 ++-- .../src/react/hooks/useReverification.ts | 16 ++++++------- packages/shared/src/react/hooks/useSession.ts | 2 ++ packages/shared/src/react/hooks/useUser.ts | 2 ++ 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/react/src/hooks/useSignIn.ts b/packages/react/src/hooks/useSignIn.ts index 308326a568d..d989846af19 100644 --- a/packages/react/src/hooks/useSignIn.ts +++ b/packages/react/src/hooks/useSignIn.ts @@ -8,6 +8,29 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid /** * The `useSignIn()` hook provides access to the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which allows you to check the current state of a sign-in attempt and manage the sign-in flow. You can use this to create a [custom sign-in flow](https://clerk.com/docs/guides/development/custom-flows/overview#sign-in-flow). * + * ### Check the current state of a sign-in + * + * The following example uses the `useSignIn()` hook to access the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which contains the current sign-in attempt status and methods to create a new sign-in attempt. The `isLoaded` property is used to handle the loading state. + * + * ``tsx {{ filename: 'src/pages/SignInPage.tsx' }} + * import { useSignIn } from '@clerk/clerk-react' + * + * export default function SignInPage() { + * const { isLoaded, signIn } = useSignIn() + * + * if (!isLoaded) { + * // Handle loading state + * return null + * } + * + * return
The current sign-in attempt status is {signIn?.status}.
+ * } + * ``` + * + * ### Create a custom sign-in flow with `useSignIn()` + * + * The `useSignIn()` hook can also be used to build fully custom sign-in flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-in flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignIn()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/guides/development/custom-flows/overview). + * */ export const useSignIn = (): UseSignInReturn => { useAssertWrappedByClerkProvider('useSignIn'); diff --git a/packages/shared/src/react/hooks/useClerk.ts b/packages/shared/src/react/hooks/useClerk.ts index 1f763d97d61..6be391f0215 100644 --- a/packages/shared/src/react/hooks/useClerk.ts +++ b/packages/shared/src/react/hooks/useClerk.ts @@ -11,10 +11,10 @@ import { useAssertWrappedByClerkProvider, useClerkInstanceContext } from '../con * The following example uses the `useClerk()` hook to access the `clerk` object. The `clerk` object is used to call the [`openSignIn()`](https://clerk.com/docs/reference/javascript/clerk#sign-in) method to open the sign-in modal. * * ```tsx {{ filename: 'src/Home.tsx' }} - * import { useClerk } from '@clerk/clerk-react'. + * import { useClerk } from '@clerk/clerk-react' * * export default function Home() { - * const clerk = useClerk(). + * const clerk = useClerk() * * return * } diff --git a/packages/shared/src/react/hooks/useReverification.ts b/packages/shared/src/react/hooks/useReverification.ts index 2367351c11b..f89cebf042b 100644 --- a/packages/shared/src/react/hooks/useReverification.ts +++ b/packages/shared/src/react/hooks/useReverification.ts @@ -37,7 +37,7 @@ type ExcludeClerkError = T extends { clerk_error: any } ? never : T; /** * @interface */ -export type NeedsReverificationParameters = { +type NeedsReverificationParameters = { cancel: () => void; complete: () => void; level: SessionVerificationLevel | undefined; @@ -48,7 +48,7 @@ export type NeedsReverificationParameters = { * * @interface */ -export type UseReverificationOptions = { +type UseReverificationOptions = { /** * A handler that is called when reverification is needed, this will opt-out of using the default UI when provided. * @@ -62,14 +62,14 @@ export type UseReverificationOptions = { /** * @interface */ -export type UseReverificationResult Promise | undefined> = ( +type UseReverificationResult Promise | undefined> = ( ...args: Parameters ) => Promise>>>; /** * @interface */ -export type UseReverification = < +type UseReverification = < Fetcher extends (...args: any[]) => Promise | undefined, Options extends UseReverificationOptions = UseReverificationOptions, >( @@ -97,7 +97,7 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) if (isReverificationHint(result)) { /** - * Create a promise + * Create a promise. */ const resolvers = createDeferredPromise(); @@ -120,7 +120,7 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) if (params.onNeedsReverification === undefined) { /** * On success resolve the pending promise - * On cancel reject the pending promise + * On cancel reject the pending promise. */ params.openUIComponent?.({ level: level, @@ -136,12 +136,12 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) } /** - * Wait until the promise from above have been resolved or rejected + * Wait until the promise from above have been resolved or rejected. */ await resolvers.promise; /** - * After the promise resolved successfully try the original request one more time + * After the promise resolved successfully try the original request one more time. */ result = await resolveResult(fetcher(...args)); } diff --git a/packages/shared/src/react/hooks/useSession.ts b/packages/shared/src/react/hooks/useSession.ts index 6f287ee37fa..b9db2e8ffc3 100644 --- a/packages/shared/src/react/hooks/useSession.ts +++ b/packages/shared/src/react/hooks/useSession.ts @@ -9,6 +9,8 @@ const hookName = `useSession`; /** * The `useSession()` hook provides access to the current user's [`Session`](https://clerk.com/docs/reference/javascript/session) object, as well as helpers for setting the active session. * + * ### Access the `Session` object + * * The following example uses the `useSession()` hook to access the `Session` object, which has the `lastActiveAt` property. The `lastActiveAt` property is a `Date` object used to show the time the session was last active. * * ```tsx {{ filename: 'src/Home.tsx' }} diff --git a/packages/shared/src/react/hooks/useUser.ts b/packages/shared/src/react/hooks/useUser.ts index 74fb27f056a..d3549ab03cd 100644 --- a/packages/shared/src/react/hooks/useUser.ts +++ b/packages/shared/src/react/hooks/useUser.ts @@ -7,6 +7,8 @@ const hookName = 'useUser'; /** * The `useUser()` hook provides access to the current user's [`User`](https://clerk.com/docs/reference/javascript/user) object, which contains all the data for a single user in your application and provides methods to manage their account. This hook also allows you to check if the user is signed in and if Clerk has loaded and initialized. * + * ### Get the current user + * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which contains the current user's data such as their full name. The `isLoaded` and `isSignedIn` properties are used to handle the loading state and to check if the user is signed in, respectively. * * ```tsx {{ filename: 'src/Example.tsx' }} From d49447e79dda7fdb606ee0085fbaffc8b0933f64 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 1 Oct 2025 15:42:40 -0600 Subject: [PATCH 05/18] Add missing code templating --- packages/react/src/hooks/useSignIn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/hooks/useSignIn.ts b/packages/react/src/hooks/useSignIn.ts index d989846af19..cce92370ad9 100644 --- a/packages/react/src/hooks/useSignIn.ts +++ b/packages/react/src/hooks/useSignIn.ts @@ -12,7 +12,7 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid * * The following example uses the `useSignIn()` hook to access the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which contains the current sign-in attempt status and methods to create a new sign-in attempt. The `isLoaded` property is used to handle the loading state. * - * ``tsx {{ filename: 'src/pages/SignInPage.tsx' }} + * ```tsx {{ filename: 'src/pages/SignInPage.tsx' }} * import { useSignIn } from '@clerk/clerk-react' * * export default function SignInPage() { From 3f1f92007bcaa2f7be7f01619f2e4a5388d13cc7 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 2 Oct 2025 11:44:40 -0600 Subject: [PATCH 06/18] Remove jsdocs changes to have in separate PR --- packages/react/docs/use-auth.md | 43 ++++++++++ packages/react/docs/use-sign-in.md | 20 +++++ packages/react/docs/use-sign-up.md | 20 +++++ packages/react/src/hooks/useAuth.ts | 35 ++++++-- packages/react/src/hooks/useSignIn.ts | 17 ++++ packages/react/src/hooks/useSignUp.ts | 17 ++++ packages/shared/docs/use-clerk.md | 15 ++++ packages/shared/docs/use-session-list.md | 0 packages/shared/docs/use-session.md | 28 +++++++ packages/shared/docs/use-user.md | 81 +++++++++++++++++++ packages/shared/src/react/hooks/useClerk.ts | 17 ++++ .../src/react/hooks/useReverification.ts | 8 +- packages/shared/src/react/hooks/useSession.ts | 18 +++++ .../shared/src/react/hooks/useSessionList.ts | 16 ++++ packages/shared/src/react/hooks/useUser.ts | 26 ++++++ 15 files changed, 351 insertions(+), 10 deletions(-) create mode 100644 packages/react/docs/use-auth.md create mode 100644 packages/react/docs/use-sign-in.md create mode 100644 packages/react/docs/use-sign-up.md create mode 100644 packages/shared/docs/use-clerk.md create mode 100644 packages/shared/docs/use-session-list.md create mode 100644 packages/shared/docs/use-session.md create mode 100644 packages/shared/docs/use-user.md diff --git a/packages/react/docs/use-auth.md b/packages/react/docs/use-auth.md new file mode 100644 index 00000000000..18ed8424cd7 --- /dev/null +++ b/packages/react/docs/use-auth.md @@ -0,0 +1,43 @@ + + +```tsx {{ filename: 'app/external-data/page.tsx' }} +'use client'; + +import { useAuth } from '@clerk/nextjs'; + +export default function ExternalDataPage() { + const { userId, sessionId, getToken, isLoaded, isSignedIn } = useAuth(); + + const fetchExternalData = async () => { + const token = await getToken(); + + // Fetch data from an external API + const response = await fetch('https://api.example.com/data', { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.json(); + }; + + if (!isLoaded) { + return
Loading...
; + } + + if (!isSignedIn) { + return
Sign in to view this page
; + } + + return ( +
+

+ Hello, {userId}! Your current active session is {sessionId}. +

+ +
+ ); +} +``` + + diff --git a/packages/react/docs/use-sign-in.md b/packages/react/docs/use-sign-in.md new file mode 100644 index 00000000000..55100d7e212 --- /dev/null +++ b/packages/react/docs/use-sign-in.md @@ -0,0 +1,20 @@ + + +```tsx {{ filename: 'app/sign-in/page.tsx' }} +'use client'; + +import { useSignIn } from '@clerk/nextjs'; + +export default function SignInPage() { + const { isLoaded, signIn } = useSignIn(); + + if (!isLoaded) { + // Handle loading state + return null; + } + + return
The current sign-in attempt status is {signIn?.status}.
; +} +``` + + diff --git a/packages/react/docs/use-sign-up.md b/packages/react/docs/use-sign-up.md new file mode 100644 index 00000000000..53d1cb10289 --- /dev/null +++ b/packages/react/docs/use-sign-up.md @@ -0,0 +1,20 @@ + + +```tsx {{ filename: 'app/sign-up/page.tsx' }} +'use client'; + +import { useSignUp } from '@clerk/nextjs'; + +export default function SignUpPage() { + const { isLoaded, signUp } = useSignUp(); + + if (!isLoaded) { + // Handle loading state + return null; + } + + return
The current sign-up attempt status is {signUp?.status}.
; +} +``` + + diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index 659b084097f..e5df2986156 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -18,19 +18,34 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid import { createGetToken, createSignOut } from './utils'; /** - * @interface - */ -export type UseAuthOptions = Record | PendingSessionOptions | undefined | null; -/** - * - * @param initialAuthStateOrOptions - An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. + * @inline */ +type UseAuthOptions = Record | PendingSessionOptions | undefined | null; /** * The `useAuth()` hook provides access to the current user's authentication state and methods to manage the active session. * + * > [!NOTE] + * > To access auth data server-side, see the [`Auth` object reference doc](https://clerk.com/docs/reference/backend/types/auth-object). + * + * + * By default, Next.js opts all routes into static rendering. If you need to opt a route or routes into dynamic rendering because you need to access the authentication data at request time, you can create a boundary by passing the `dynamic` prop to ``. See the [guide on rendering modes](https://clerk.com/docs/guides/development/rendering-modes) for more information, including code examples. + * + * + * @unionReturnHeadings + * ["Initialization", "Signed out", "Signed in (no active organization)", "Signed in (with active organization)"] + * + * @param [initialAuthStateOrOptions] - An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. + * + * @function + * + * @example + * * The following example demonstrates how to use the `useAuth()` hook to access the current auth state, like whether the user is signed in or not. It also includes a basic example for using the `getToken()` method to retrieve a session token for fetching data from an external resource. * + * + * + * * ```tsx {{ filename: 'src/pages/ExternalDataPage.tsx' }} * import { useAuth } from '@clerk/clerk-react' * @@ -68,6 +83,14 @@ export type UseAuthOptions = Record | PendingSessionOptions | undef * ) * } * ``` + * + * + * + * + * {@include ../../docs/use-auth.md#nextjs-01} + * + * + * */ export const useAuth = (initialAuthStateOrOptions: UseAuthOptions = {}): UseAuthReturn => { useAssertWrappedByClerkProvider('useAuth'); diff --git a/packages/react/src/hooks/useSignIn.ts b/packages/react/src/hooks/useSignIn.ts index cce92370ad9..2b7fb2e8082 100644 --- a/packages/react/src/hooks/useSignIn.ts +++ b/packages/react/src/hooks/useSignIn.ts @@ -8,10 +8,17 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid /** * The `useSignIn()` hook provides access to the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which allows you to check the current state of a sign-in attempt and manage the sign-in flow. You can use this to create a [custom sign-in flow](https://clerk.com/docs/guides/development/custom-flows/overview#sign-in-flow). * + * @unionReturnHeadings + * ["Initialization", "Loaded"] + * + * @example * ### Check the current state of a sign-in * * The following example uses the `useSignIn()` hook to access the [`SignIn`](https://clerk.com/docs/reference/javascript/sign-in) object, which contains the current sign-in attempt status and methods to create a new sign-in attempt. The `isLoaded` property is used to handle the loading state. * + * + * + * * ```tsx {{ filename: 'src/pages/SignInPage.tsx' }} * import { useSignIn } from '@clerk/clerk-react' * @@ -27,10 +34,20 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid * } * ``` * + * + * + * + * {@include ../../docs/use-sign-in.md#nextjs-01} + * + * + * + * + * @example * ### Create a custom sign-in flow with `useSignIn()` * * The `useSignIn()` hook can also be used to build fully custom sign-in flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-in flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignIn()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/guides/development/custom-flows/overview). * + * ```empty``` */ export const useSignIn = (): UseSignInReturn => { useAssertWrappedByClerkProvider('useSignIn'); diff --git a/packages/react/src/hooks/useSignUp.ts b/packages/react/src/hooks/useSignUp.ts index c6ffb8fbba7..5b3af8e7804 100644 --- a/packages/react/src/hooks/useSignUp.ts +++ b/packages/react/src/hooks/useSignUp.ts @@ -8,10 +8,17 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid /** * The `useSignUp()` hook provides access to the [`SignUp`](https://clerk.com/docs/reference/javascript/sign-up) object, which allows you to check the current state of a sign-up attempt and manage the sign-up flow. You can use this to create a [custom sign-up flow](https://clerk.com/docs/guides/development/custom-flows/overview#sign-up-flow). * + * @unionReturnHeadings + * ["Initialization", "Loaded"] + * + * @example * ### Check the current state of a sign-up * * The following example uses the `useSignUp()` hook to access the [`SignUp`](https://clerk.com/docs/reference/javascript/sign-up) object, which contains the current sign-up attempt status and methods to create a new sign-up attempt. The `isLoaded` property is used to handle the loading state. * + * + * + * * ```tsx {{ filename: 'src/pages/SignUpPage.tsx' }} * import { useSignUp } from '@clerk/clerk-react' * @@ -27,10 +34,20 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid * } * ``` * + * + * + * + * {@include ../../docs/use-sign-up.md#nextjs-01} + * + * + * + * + * @example * ### Create a custom sign-up flow with `useSignUp()` * * The `useSignUp()` hook can also be used to build fully custom sign-up flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-up flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignUp()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/guides/development/custom-flows/overview). * + * ```empty``` */ export const useSignUp = (): UseSignUpReturn => { useAssertWrappedByClerkProvider('useSignUp'); diff --git a/packages/shared/docs/use-clerk.md b/packages/shared/docs/use-clerk.md new file mode 100644 index 00000000000..839672b69cf --- /dev/null +++ b/packages/shared/docs/use-clerk.md @@ -0,0 +1,15 @@ + + +```tsx {{ filename: 'app/page.tsx' }} +'use client'; + +import { useClerk } from '@clerk/nextjs'; + +export default function HomePage() { + const clerk = useClerk(); + + return ; +} +``` + + diff --git a/packages/shared/docs/use-session-list.md b/packages/shared/docs/use-session-list.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/shared/docs/use-session.md b/packages/shared/docs/use-session.md new file mode 100644 index 00000000000..95be5884665 --- /dev/null +++ b/packages/shared/docs/use-session.md @@ -0,0 +1,28 @@ + + +```tsx {{ filename: 'app/page.tsx' }} +'use client'; + +import { useSession } from '@clerk/nextjs'; + +export default function HomePage() { + const { isLoaded, session, isSignedIn } = useSession(); + + if (!isLoaded) { + // Handle loading state + return null; + } + if (!isSignedIn) { + // Handle signed out state + return null; + } + + return ( +
+

This session has been active since {session.lastActiveAt.toLocaleString()}

+
+ ); +} +``` + + diff --git a/packages/shared/docs/use-user.md b/packages/shared/docs/use-user.md new file mode 100644 index 00000000000..106804ac014 --- /dev/null +++ b/packages/shared/docs/use-user.md @@ -0,0 +1,81 @@ + + +```tsx {{ filename: 'app/page.tsx' }} +'use client'; + +import { useUser } from '@clerk/nextjs'; + +export default function HomePage() { + const { isSignedIn, isLoaded, user } = useUser(); + + if (!isLoaded) { + // Handle loading state + return null; + } + + if (!isSignedIn) return null; + + const updateUser = async () => { + await user.update({ + firstName: 'John', + lastName: 'Doe', + }); + }; + + return ( + <> + +

user.firstName: {user.firstName}

+

user.lastName: {user.lastName}

+ + ); +} +``` + + + + + +```tsx {{ filename: 'app/page.tsx' }} +'use client'; + +import { useUser } from '@clerk/nextjs'; + +export default function HomePage() { + const { isSignedIn, isLoaded, user } = useUser(); + + if (!isLoaded) { + // Handle loading state + return null; + } + + if (!isSignedIn) return null; + + const updateUser = async () => { + // Update data via an API endpoint + const updateMetadata = await fetch('/api/updateMetadata', { + method: 'POST', + body: JSON.stringify({ + role: 'admin', + }), + }); + + // Check if the update was successful + if ((await updateMetadata.json()).message !== 'success') { + throw new Error('Error updating'); + } + + // If the update was successful, reload the user data + await user.reload(); + }; + + return ( + <> + +

user role: {user.publicMetadata.role}

+ + ); +} +``` + + diff --git a/packages/shared/src/react/hooks/useClerk.ts b/packages/shared/src/react/hooks/useClerk.ts index 6be391f0215..87f36ca3c2c 100644 --- a/packages/shared/src/react/hooks/useClerk.ts +++ b/packages/shared/src/react/hooks/useClerk.ts @@ -8,8 +8,17 @@ import { useAssertWrappedByClerkProvider, useClerkInstanceContext } from '../con * * The `useClerk()` hook provides access to the [`Clerk`](https://clerk.com/docs/reference/javascript/clerk) object, allowing you to build alternatives to any Clerk Component. * + * @function + * + * @returns The `useClerk()` hook returns the `Clerk` object, which includes all the methods and properties listed in the [`Clerk` reference](https://clerk.com/docs/reference/javascript/clerk). + * + * @example + * * The following example uses the `useClerk()` hook to access the `clerk` object. The `clerk` object is used to call the [`openSignIn()`](https://clerk.com/docs/reference/javascript/clerk#sign-in) method to open the sign-in modal. * + * + * + * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useClerk } from '@clerk/clerk-react' * @@ -19,6 +28,14 @@ import { useAssertWrappedByClerkProvider, useClerkInstanceContext } from '../con * return * } * ``` + * + * + * + * + * {@include ../../../docs/use-clerk.md#nextjs-01} + * + * + * */ export const useClerk = (): LoadedClerk => { useAssertWrappedByClerkProvider('useClerk'); diff --git a/packages/shared/src/react/hooks/useReverification.ts b/packages/shared/src/react/hooks/useReverification.ts index f89cebf042b..31f3edbdf88 100644 --- a/packages/shared/src/react/hooks/useReverification.ts +++ b/packages/shared/src/react/hooks/useReverification.ts @@ -97,7 +97,7 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) if (isReverificationHint(result)) { /** - * Create a promise. + * Create a promise */ const resolvers = createDeferredPromise(); @@ -120,7 +120,7 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) if (params.onNeedsReverification === undefined) { /** * On success resolve the pending promise - * On cancel reject the pending promise. + * On cancel reject the pending promise */ params.openUIComponent?.({ level: level, @@ -136,12 +136,12 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) } /** - * Wait until the promise from above have been resolved or rejected. + * Wait until the promise from above have been resolved or rejected */ await resolvers.promise; /** - * After the promise resolved successfully try the original request one more time. + * After the promise resolved successfully try the original request one more time */ result = await resolveResult(fetcher(...args)); } diff --git a/packages/shared/src/react/hooks/useSession.ts b/packages/shared/src/react/hooks/useSession.ts index b9db2e8ffc3..df656dc6042 100644 --- a/packages/shared/src/react/hooks/useSession.ts +++ b/packages/shared/src/react/hooks/useSession.ts @@ -9,10 +9,20 @@ const hookName = `useSession`; /** * The `useSession()` hook provides access to the current user's [`Session`](https://clerk.com/docs/reference/javascript/session) object, as well as helpers for setting the active session. * + * @unionReturnHeadings + * ["Initialization", "Signed out", "Signed in"] + * + * @function + * + * @param [options] - An object containing options for the `useSession()` hook. + * @example * ### Access the `Session` object * * The following example uses the `useSession()` hook to access the `Session` object, which has the `lastActiveAt` property. The `lastActiveAt` property is a `Date` object used to show the time the session was last active. * + * + * + * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useSession } from '@clerk/clerk-react' * @@ -35,6 +45,14 @@ const hookName = `useSession`; * ) * } * ``` + * + * + * + * + * {@include ../../../docs/use-session.md#nextjs-01} + * + * + * */ export const useSession: UseSession = () => { useAssertWrappedByClerkProvider(hookName); diff --git a/packages/shared/src/react/hooks/useSessionList.ts b/packages/shared/src/react/hooks/useSessionList.ts index eb6a357fdf3..6d0ee1b0bd1 100644 --- a/packages/shared/src/react/hooks/useSessionList.ts +++ b/packages/shared/src/react/hooks/useSessionList.ts @@ -7,10 +7,19 @@ const hookName = 'useSessionList'; /** * The `useSessionList()` hook returns an array of [`Session`](https://clerk.com/docs/reference/javascript/session) objects that have been registered on the client device. * + * @unionReturnHeadings + * ["Initialization", "Loaded"] + * + * @function + * + * @example * ### Get a list of sessions * * The following example uses `useSessionList()` to get a list of sessions that have been registered on the client device. The `sessions` property is used to show the number of times the user has visited the page. * + * + * + * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useSessionList } from '@clerk/clerk-react' * @@ -30,6 +39,13 @@ const hookName = 'useSessionList'; * } * ``` * + * + * + * + * {@include ../../../docs/use-session-list.md#nextjs-01} + * + * + * */ export const useSessionList = (): UseSessionListReturn => { useAssertWrappedByClerkProvider(hookName); diff --git a/packages/shared/src/react/hooks/useUser.ts b/packages/shared/src/react/hooks/useUser.ts index d3549ab03cd..a25367b8c89 100644 --- a/packages/shared/src/react/hooks/useUser.ts +++ b/packages/shared/src/react/hooks/useUser.ts @@ -7,6 +7,10 @@ const hookName = 'useUser'; /** * The `useUser()` hook provides access to the current user's [`User`](https://clerk.com/docs/reference/javascript/user) object, which contains all the data for a single user in your application and provides methods to manage their account. This hook also allows you to check if the user is signed in and if Clerk has loaded and initialized. * + * @unionReturnHeadings + * ["Initialization", "Signed out", "Signed in"] + * + * @example * ### Get the current user * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which contains the current user's data such as their full name. The `isLoaded` and `isSignedIn` properties are used to handle the loading state and to check if the user is signed in, respectively. @@ -29,10 +33,14 @@ const hookName = 'useUser'; * } * ``` * + * @example * ### Update user data * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which calls the [`update()`](https://clerk.com/docs/reference/javascript/user#update) method to update the current user's information. * + * + * + * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useUser } from '@clerk/clerk-react' * @@ -62,11 +70,22 @@ const hookName = 'useUser'; * ) * } * ``` + * + * + * + * {@include ../../../docs/use-user.md#nextjs-01} + * + * + * * + * @example * ### Reload user data * * The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/javascript/user) object, which calls the [`reload()`](https://clerk.com/docs/reference/javascript/user#reload) method to get the latest user's information. * + * + * + * * ```tsx {{ filename: 'src/Home.tsx' }} * import { useUser } from '@clerk/clerk-react' * @@ -107,6 +126,13 @@ const hookName = 'useUser'; * } * ``` * + * + * + * + * {@include ../../../docs/use-user.md#nextjs-02} + * + * + * */ export function useUser(): UseUserReturn { useAssertWrappedByClerkProvider(hookName); From 3cc87f956b8889fe1c360738adb5ac8d4e9870be Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 2 Oct 2025 11:55:45 -0600 Subject: [PATCH 07/18] Bring back session list --- packages/shared/docs/use-session-list.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/shared/docs/use-session-list.md b/packages/shared/docs/use-session-list.md index e69de29bb2d..c4441a59b95 100644 --- a/packages/shared/docs/use-session-list.md +++ b/packages/shared/docs/use-session-list.md @@ -0,0 +1,24 @@ + + +```tsx {{ filename: 'app/page.tsx' }} +'use client'; + +import { useSessionList } from '@clerk/nextjs'; + +export default function HomePage() { + const { isLoaded, sessions } = useSessionList(); + + if (!isLoaded) { + // Handle loading state + return null; + } + + return ( +
+

Welcome back. You've been here {sessions.length} times before.

+
+ ); +} +``` + + From 16f9663c2a7e377e989734f7040b26c6e46f3c76 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 2 Oct 2025 14:38:19 -0600 Subject: [PATCH 08/18] Fix setActive returns --- packages/types/src/hooks.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index e710e059e54..5cfd7dc62b5 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -1,4 +1,4 @@ -import type { SetActive, SignOut } from './clerk'; +import type { SetActiveParams, SignOut } from './clerk'; import type { ActClaim, JwtPayload } from './jwtv2'; import type { OrganizationCustomRoleKey } from './organizationMembership'; import type { @@ -140,7 +140,7 @@ export type UseSignInReturn = | { isLoaded: true; signIn: SignInResource; - setActive: SetActive; + setActive: (setActiveParams: SetActiveParams) => Promise; }; /** @@ -165,7 +165,7 @@ export type UseSignUpReturn = | { isLoaded: true; signUp: SignUpResource; - setActive: SetActive; + setActive: (setActiveParams: SetActiveParams) => Promise; }; /** @@ -220,7 +220,7 @@ export type UseSessionListReturn = | { isLoaded: true; sessions: SessionResource[]; - setActive: SetActive; + setActive: (setActiveParams: SetActiveParams) => Promise; }; /** From febc294ea8c478cb5ac2d0d3ac9ea7e0a003d3ad Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 2 Oct 2025 16:03:41 -0600 Subject: [PATCH 09/18] Fix returns for useAuth --- packages/types/src/hooks.ts | 47 +++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 5cfd7dc62b5..59d80dc85fe 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -1,16 +1,15 @@ -import type { SetActiveParams, SignOut } from './clerk'; -import type { ActClaim, JwtPayload } from './jwtv2'; -import type { OrganizationCustomRoleKey } from './organizationMembership'; +import type { SetActiveParams, SignOutCallback, SignOutOptions } from './clerk'; +import type { JwtPayload } from './jwtv2'; import type { + CheckAuthorizationParamsWithCustomPermissions, CheckAuthorizationWithCustomPermissions, - GetToken, + GetTokenOptions, SessionResource, SignedInSessionResource, } from './session'; import type { SignInResource } from './signIn'; import type { SignUpResource } from './signUp'; import type { UserResource } from './user'; - /** * @inline */ @@ -69,11 +68,14 @@ export type UseAuthReturn = /** * A function that signs out the current user. Returns a promise that resolves when complete. See the [reference doc](https://clerk.com/docs/reference/javascript/clerk#sign-out). */ - signOut: SignOut; + signOut: { + (options?: SignOutOptions): Promise; + (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; + }; /** * A function that retrieves the current user's session token or a custom JWT template. Returns a promise that resolves to the token. See the [reference doc](https://clerk.com/docs/reference/javascript/session#get-token). */ - getToken: GetToken; + getToken: (options?: GetTokenOptions) => Promise; } | { isLoaded: true; @@ -86,8 +88,11 @@ export type UseAuthReturn = orgRole: null; orgSlug: null; has: CheckAuthorizationWithoutOrgOrUser; - signOut: SignOut; - getToken: GetToken; + signOut: { + (options?: SignOutOptions): Promise; + (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; + }; + getToken: (options?: GetTokenOptions) => Promise; } | { isLoaded: true; @@ -95,13 +100,16 @@ export type UseAuthReturn = userId: string; sessionId: string; sessionClaims: JwtPayload; - actor: ActClaim | null; + actor: { [x: string]: unknown; sub: string } | null; orgId: null; orgRole: null; orgSlug: null; - has: CheckAuthorizationWithCustomPermissions; - signOut: SignOut; - getToken: GetToken; + has: (isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions) => boolean; + signOut: { + (options?: SignOutOptions): Promise; + (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; + }; + getToken: (options?: GetTokenOptions) => Promise; } | { isLoaded: true; @@ -109,13 +117,16 @@ export type UseAuthReturn = userId: string; sessionId: string; sessionClaims: JwtPayload; - actor: ActClaim | null; + actor: { [x: string]: unknown; sub: string } | null; orgId: string; - orgRole: OrganizationCustomRoleKey; + orgRole: string; orgSlug: string | null; - has: CheckAuthorizationWithCustomPermissions; - signOut: SignOut; - getToken: GetToken; + has: (isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions) => boolean; + signOut: { + (options?: SignOutOptions): Promise; + (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; + }; + getToken: (options?: GetTokenOptions) => Promise; }; /** From c32a435079ac9e537842efb50be1515f66ad0096 Mon Sep 17 00:00:00 2001 From: Nick Wylynko Date: Sat, 4 Oct 2025 06:19:22 +0800 Subject: [PATCH 10/18] Update the custom theme to get the useAuth options markdown generated correctly --- .typedoc/custom-theme.mjs | 34 +++++++++++++++++++++++++++++ packages/react/src/hooks/useAuth.ts | 5 +++-- packages/types/src/session.ts | 1 + typedoc.config.mjs | 2 ++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 1ce9c403d22..48131646583 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -296,10 +296,33 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { }, /** * This hides the "Type parameters" section, the declaration title, and the "Type declaration" heading from the output + * Unless the @includeType tag is present, in which case it shows the type in a parameter table format * @param {import('typedoc').DeclarationReflection} model * @param {{ headingLevel: number, nested?: boolean }} options */ declaration: (model, options = { headingLevel: 2, nested: false }) => { + // Check if @includeType tag is present + const showTypeTag = model.comment?.getTag('@includeType'); + + if (showTypeTag) { + // If a name has been provided, use it, otherwise use the type name + const name = showTypeTag.content[0].text ?? model.name; + + // Generate parameter table format for types with @includeType + let typeStr = model.type ? this.partials.someType(model.type) : ''; + + const description = model.comment?.summary ? this.helpers.getCommentParts(model.comment.summary) : ''; + + // Create the markdown table + const md = [ + '| Parameter | Type | Description |', + '| --------- | ---- | ----------- |', + `| \`${name}?\` | ${typeStr} | ${description} |`, + ].join('\n'); + + return md; + } + // Create a local override const localPartials = { ...this.partials, @@ -499,6 +522,17 @@ ${tabs} const description = Array.isArray(summary) ? summary.reduce((acc, curr) => acc + (curr.text || ''), '') : ''; return '| ' + '`' + escapeChars(name) + '`' + ' | ' + typeStr + ' | ' + description + ' |'; }, + /** + * @param {import('typedoc').ReferenceType} model + */ + referenceType: model => { + // If @unionInline tag is present, we grab the type itself and return it, instead of a link to the type + if (model.reflection && model.reflection.isDeclaration() && model.reflection.comment?.getTag('@unionInline')) { + return superPartials.declarationType(model.reflection); + } + + return superPartials.referenceType(model); + }, }; } } diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index e5df2986156..6e332da49bd 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -18,9 +18,10 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid import { createGetToken, createSignOut } from './utils'; /** - * @inline + * An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. + * @includeType initialAuthStateOrOptions */ -type UseAuthOptions = Record | PendingSessionOptions | undefined | null; +export type UseAuthOptions = Record | PendingSessionOptions | undefined | null; /** * The `useAuth()` hook provides access to the current user's authentication state and methods to manage the active session. diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index a189d85c73c..d3a81be31e5 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -30,6 +30,7 @@ import type { Autocomplete } from './utils'; /** * @inline + * @unionInline */ export type PendingSessionOptions = { /** diff --git a/typedoc.config.mjs b/typedoc.config.mjs index d19227fc2e1..a457ec91c52 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -9,6 +9,8 @@ const CUSTOM_BLOCK_TAGS = [ '@paramExtension', '@experimental', '@hideReturns', + '@includeType', + '@unionInline', ]; /** From 263dba2feb06aa247f6c3b1c28c25971d3c31445 Mon Sep 17 00:00:00 2001 From: Nick Wylynko Date: Wed, 8 Oct 2025 04:13:49 +0800 Subject: [PATCH 11/18] replace @unionInline with @embedType that covers more type inlining --- .typedoc/custom-theme.mjs | 31 +++++++++++++++++++++++++--- packages/types/src/clerk.ts | 1 + packages/types/src/hooks.ts | 39 +++++++++++++---------------------- packages/types/src/session.ts | 13 +++++++++--- typedoc.config.mjs | 3 +-- 5 files changed, 54 insertions(+), 33 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 48131646583..b174da518dd 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -526,9 +526,34 @@ ${tabs} * @param {import('typedoc').ReferenceType} model */ referenceType: model => { - // If @unionInline tag is present, we grab the type itself and return it, instead of a link to the type - if (model.reflection && model.reflection.isDeclaration() && model.reflection.comment?.getTag('@unionInline')) { - return superPartials.declarationType(model.reflection); + if ( + model.reflection && + model.reflection.isDeclaration() && + model.reflection.comment?.modifierTags?.has('@embedType') + ) { + // Case 1: Callable interfaces with signatures directly on the reflection (e.g., SignOut) + // Structure: signatures array exists, no type property + if (model.reflection.signatures && !model.reflection.type) { + return this.partials.functionType(model.reflection.signatures); + } + + // Case 2: Object types with children (e.g., PendingSessionOptions) + // Structure: children array exists, no type property + if (model.reflection.children && !model.reflection.type) { + return superPartials.declarationType(model.reflection); + } + + // Case 3: Function type aliases with nested signatures (e.g., GetToken, CheckAuthorizationFn) + // Structure: type.type === 'reflection' with type.declaration.signatures + if (model.reflection.type?.type === 'reflection' && model.reflection.type.declaration?.signatures) { + return this.partials.functionType(model.reflection.type.declaration.signatures); + } + + // Case 4: Other type aliases with a type property + // Fallback for any other type structure + if (model.reflection.type) { + return this.partials.someType(model.reflection.type); + } } return superPartials.referenceType(model); diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index fcf84df542f..b050ddda37f 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -139,6 +139,7 @@ export type SignOutOptions = { /** * @inline + * @embedType */ export interface SignOut { (options?: SignOutOptions): Promise; diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 59d80dc85fe..0d8d4eb383b 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -1,9 +1,8 @@ -import type { SetActiveParams, SignOutCallback, SignOutOptions } from './clerk'; +import type { SetActiveParams, SignOut } from './clerk'; import type { JwtPayload } from './jwtv2'; import type { - CheckAuthorizationParamsWithCustomPermissions, CheckAuthorizationWithCustomPermissions, - GetTokenOptions, + GetToken, SessionResource, SignedInSessionResource, } from './session'; @@ -12,10 +11,12 @@ import type { SignUpResource } from './signUp'; import type { UserResource } from './user'; /** * @inline + * @embedType */ type CheckAuthorizationSignedOut = undefined; /** * @inline + * @embedType */ type CheckAuthorizationWithoutOrgOrUser = (params: Parameters[0]) => false; @@ -68,14 +69,11 @@ export type UseAuthReturn = /** * A function that signs out the current user. Returns a promise that resolves when complete. See the [reference doc](https://clerk.com/docs/reference/javascript/clerk#sign-out). */ - signOut: { - (options?: SignOutOptions): Promise; - (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; - }; + signOut: SignOut; /** * A function that retrieves the current user's session token or a custom JWT template. Returns a promise that resolves to the token. See the [reference doc](https://clerk.com/docs/reference/javascript/session#get-token). */ - getToken: (options?: GetTokenOptions) => Promise; + getToken: GetToken; } | { isLoaded: true; @@ -88,11 +86,8 @@ export type UseAuthReturn = orgRole: null; orgSlug: null; has: CheckAuthorizationWithoutOrgOrUser; - signOut: { - (options?: SignOutOptions): Promise; - (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; - }; - getToken: (options?: GetTokenOptions) => Promise; + signOut: SignOut; + getToken: GetToken; } | { isLoaded: true; @@ -104,12 +99,9 @@ export type UseAuthReturn = orgId: null; orgRole: null; orgSlug: null; - has: (isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions) => boolean; - signOut: { - (options?: SignOutOptions): Promise; - (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; - }; - getToken: (options?: GetTokenOptions) => Promise; + has: CheckAuthorizationWithCustomPermissions; + signOut: SignOut; + getToken: GetToken; } | { isLoaded: true; @@ -121,12 +113,9 @@ export type UseAuthReturn = orgId: string; orgRole: string; orgSlug: string | null; - has: (isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions) => boolean; - signOut: { - (options?: SignOutOptions): Promise; - (signOutCallback?: SignOutCallback, options?: SignOutOptions): Promise; - }; - getToken: (options?: GetTokenOptions) => Promise; + has: CheckAuthorizationWithCustomPermissions; + signOut: SignOut; + getToken: GetToken; }; /** diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index d3a81be31e5..0db51f7ab2d 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -30,7 +30,7 @@ import type { Autocomplete } from './utils'; /** * @inline - * @unionInline + * @embedType */ export type PendingSessionOptions = { /** @@ -44,10 +44,16 @@ type DisallowSystemPermissions

= P extends `${OrganizationSyst ? 'System permissions are not included in session claims and cannot be used on the server-side' : P; -/** @inline */ +/** + * @inline + * @embedType + */ export type CheckAuthorizationFn = (isAuthorizedParams: Params) => boolean; -/** @inline */ +/** + * @inline + * @embedType + */ export type CheckAuthorizationWithCustomPermissions = CheckAuthorizationFn; @@ -345,6 +351,7 @@ export type GetTokenOptions = { }; /** * @inline + * @embedType */ export type GetToken = (options?: GetTokenOptions) => Promise; diff --git a/typedoc.config.mjs b/typedoc.config.mjs index a457ec91c52..87477eb70e0 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -10,7 +10,6 @@ const CUSTOM_BLOCK_TAGS = [ '@experimental', '@hideReturns', '@includeType', - '@unionInline', ]; /** @@ -111,7 +110,7 @@ const config = { excludeNotDocumented: true, gitRevision: 'main', blockTags: [...OptionDefaults.blockTags, ...CUSTOM_BLOCK_TAGS], - modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental')], + modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), '@embedType'], exclude: ['src/**/*.test.ts', 'src/**/*.test.tsx'], readme: 'none', disableGit: true, From 316c8611c23a0e02a1b679a191cb6b1b946bf243 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Tue, 7 Oct 2025 15:42:23 -0600 Subject: [PATCH 12/18] Fix SetActive return after Nick fix --- packages/types/src/clerk.ts | 1 + packages/types/src/hooks.ts | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index b050ddda37f..34fd79a07bf 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -1265,6 +1265,7 @@ export type SetActiveParams = { /** * @inline + * @embedType */ export type SetActive = (setActiveParams: SetActiveParams) => Promise; diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 0d8d4eb383b..eea77605524 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -1,4 +1,4 @@ -import type { SetActiveParams, SignOut } from './clerk'; +import type { SetActive, SignOut } from './clerk'; import type { JwtPayload } from './jwtv2'; import type { CheckAuthorizationWithCustomPermissions, @@ -140,7 +140,7 @@ export type UseSignInReturn = | { isLoaded: true; signIn: SignInResource; - setActive: (setActiveParams: SetActiveParams) => Promise; + setActive: SetActive; }; /** @@ -165,7 +165,7 @@ export type UseSignUpReturn = | { isLoaded: true; signUp: SignUpResource; - setActive: (setActiveParams: SetActiveParams) => Promise; + setActive: SetActive; }; /** @@ -220,7 +220,7 @@ export type UseSessionListReturn = | { isLoaded: true; sessions: SessionResource[]; - setActive: (setActiveParams: SetActiveParams) => Promise; + setActive: SetActive; }; /** From 40dbc8b2838e61c5fbb14d1c27af708abc4fecf8 Mon Sep 17 00:00:00 2001 From: Nick Wylynko Date: Thu, 9 Oct 2025 01:10:29 +0800 Subject: [PATCH 13/18] Add new extract returns and params script --- .typedoc/custom-theme.mjs | 58 --------- .typedoc/extract-returns-and-params.mjs | 164 ++++++++++++++++++++++++ package.json | 2 +- packages/react/src/hooks/useAuth.ts | 5 +- packages/types/src/clerk.ts | 2 - packages/types/src/hooks.ts | 14 +- packages/types/src/session.ts | 3 - typedoc.config.mjs | 3 +- 8 files changed, 174 insertions(+), 77 deletions(-) create mode 100644 .typedoc/extract-returns-and-params.mjs diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index b174da518dd..6d2458e6bbb 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -301,28 +301,6 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {{ headingLevel: number, nested?: boolean }} options */ declaration: (model, options = { headingLevel: 2, nested: false }) => { - // Check if @includeType tag is present - const showTypeTag = model.comment?.getTag('@includeType'); - - if (showTypeTag) { - // If a name has been provided, use it, otherwise use the type name - const name = showTypeTag.content[0].text ?? model.name; - - // Generate parameter table format for types with @includeType - let typeStr = model.type ? this.partials.someType(model.type) : ''; - - const description = model.comment?.summary ? this.helpers.getCommentParts(model.comment.summary) : ''; - - // Create the markdown table - const md = [ - '| Parameter | Type | Description |', - '| --------- | ---- | ----------- |', - `| \`${name}?\` | ${typeStr} | ${description} |`, - ].join('\n'); - - return md; - } - // Create a local override const localPartials = { ...this.partials, @@ -522,42 +500,6 @@ ${tabs} const description = Array.isArray(summary) ? summary.reduce((acc, curr) => acc + (curr.text || ''), '') : ''; return '| ' + '`' + escapeChars(name) + '`' + ' | ' + typeStr + ' | ' + description + ' |'; }, - /** - * @param {import('typedoc').ReferenceType} model - */ - referenceType: model => { - if ( - model.reflection && - model.reflection.isDeclaration() && - model.reflection.comment?.modifierTags?.has('@embedType') - ) { - // Case 1: Callable interfaces with signatures directly on the reflection (e.g., SignOut) - // Structure: signatures array exists, no type property - if (model.reflection.signatures && !model.reflection.type) { - return this.partials.functionType(model.reflection.signatures); - } - - // Case 2: Object types with children (e.g., PendingSessionOptions) - // Structure: children array exists, no type property - if (model.reflection.children && !model.reflection.type) { - return superPartials.declarationType(model.reflection); - } - - // Case 3: Function type aliases with nested signatures (e.g., GetToken, CheckAuthorizationFn) - // Structure: type.type === 'reflection' with type.declaration.signatures - if (model.reflection.type?.type === 'reflection' && model.reflection.type.declaration?.signatures) { - return this.partials.functionType(model.reflection.type.declaration.signatures); - } - - // Case 4: Other type aliases with a type property - // Fallback for any other type structure - if (model.reflection.type) { - return this.partials.someType(model.reflection.type); - } - } - - return superPartials.referenceType(model); - }, }; } } diff --git a/.typedoc/extract-returns-and-params.mjs b/.typedoc/extract-returns-and-params.mjs new file mode 100644 index 00000000000..3dbda8cfdfc --- /dev/null +++ b/.typedoc/extract-returns-and-params.mjs @@ -0,0 +1,164 @@ +// @ts-check +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Extracts the "## Returns" section from a markdown file and writes it to a separate file. + * @param {string} filePath - The path to the markdown file + * @returns {boolean} True if a file was created + */ +function extractReturnsSection(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + + // Find the "## Returns" section + const returnsStart = content.indexOf('## Returns'); + + if (returnsStart === -1) { + return false; // No Returns section found + } + + // Find the next heading after "## Returns" (or end of file) + const afterReturns = content.slice(returnsStart + 10); // Skip past "## Returns" + const nextHeadingMatch = afterReturns.match(/\n## /); + const returnsEnd = + nextHeadingMatch && typeof nextHeadingMatch.index === 'number' + ? returnsStart + 10 + nextHeadingMatch.index + : content.length; + + // Extract the Returns section and trim trailing whitespace + const returnsContent = content.slice(returnsStart, returnsEnd).trimEnd(); + + // Generate the new filename: use-auth.mdx -> use-auth-return.mdx + const fileName = path.basename(filePath, '.mdx'); + const dirName = path.dirname(filePath); + const newFilePath = path.join(dirName, `${fileName}-return.mdx`); + + // Write the extracted Returns section to the new file + fs.writeFileSync(newFilePath, returnsContent, 'utf-8'); + + console.log(`[extract-returns] Created ${path.relative(process.cwd(), newFilePath)}`); + return true; +} + +/** + * Extracts the "## Parameters" section from a markdown file and writes it to a separate file. + * @param {string} filePath - The path to the markdown file + * @param {string} dirName - The directory containing the files + * @returns {boolean} True if a file was created + */ +function extractParametersSection(filePath, dirName) { + const content = fs.readFileSync(filePath, 'utf-8'); + const fileName = path.basename(filePath, '.mdx'); + + // Always use -params suffix + const suffix = '-params'; + const targetFileName = `${fileName}${suffix}.mdx`; + const propsFileName = `${fileName}-props.mdx`; + + // Delete any existing -props file (TypeDoc-generated) + const propsFilePath = path.join(dirName, propsFileName); + if (fs.existsSync(propsFilePath)) { + fs.unlinkSync(propsFilePath); + console.log(`[extract-returns] Deleted ${path.relative(process.cwd(), propsFilePath)}`); + } + + // Find the "## Parameters" section + const paramsStart = content.indexOf('## Parameters'); + + if (paramsStart === -1) { + return false; // No Parameters section found + } + + // Find the next heading after "## Parameters" (or end of file) + const afterParams = content.slice(paramsStart + 13); // Skip past "## Parameters" + const nextHeadingMatch = afterParams.match(/\n## /); + const paramsEnd = + nextHeadingMatch && typeof nextHeadingMatch.index === 'number' + ? paramsStart + 13 + nextHeadingMatch.index + : content.length; + + // Extract the Parameters section and trim trailing whitespace + const paramsContent = content.slice(paramsStart, paramsEnd).trimEnd(); + + // Write to new file + const newFilePath = path.join(dirName, targetFileName); + fs.writeFileSync(newFilePath, paramsContent, 'utf-8'); + + console.log(`[extract-returns] Created ${path.relative(process.cwd(), newFilePath)}`); + return true; +} + +/** + * Recursively reads all .mdx files in a directory, excluding generated files + * @param {string} dir - The directory to read + * @returns {string[]} Array of file paths + */ +function getAllMdxFiles(dir) { + /** @type {string[]} */ + const files = []; + + if (!fs.existsSync(dir)) { + return files; + } + + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + files.push(...getAllMdxFiles(fullPath)); + } else if (entry.isFile() && entry.name.endsWith('.mdx')) { + // Exclude generated files + const isGenerated = + entry.name.endsWith('-return.mdx') || entry.name.endsWith('-params.mdx') || entry.name.endsWith('-props.mdx'); + if (!isGenerated) { + files.push(fullPath); + } + } + } + + return files; +} + +/** + * Main function to process all clerk-react files + */ +function main() { + const packages = ['clerk-react']; + const dirs = packages.map(folder => path.join(__dirname, 'temp-docs', folder)); + + for (const dir of dirs) { + if (!fs.existsSync(dir)) { + console.log(`[extract-returns] ${dir} directory not found, skipping extraction`); + continue; + } + + const mdxFiles = getAllMdxFiles(dir); + console.log(`[extract-returns] Processing ${mdxFiles.length} files in ${dir}/`); + + let returnsCount = 0; + let paramsCount = 0; + + for (const filePath of mdxFiles) { + // Extract Returns sections + if (extractReturnsSection(filePath)) { + returnsCount++; + } + + // Extract Parameters sections + if (extractParametersSection(filePath, dir)) { + paramsCount++; + } + } + + console.log(`[extract-returns] Extracted ${returnsCount} Returns sections`); + console.log(`[extract-returns] Extracted ${paramsCount} Parameters sections`); + } +} + +main(); diff --git a/package.json b/package.json index 1320877a2a8..a4f9e4239e8 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "test:typedoc": "pnpm typedoc:generate && cd ./.typedoc && vitest run", "turbo:clean": "turbo daemon clean", "typedoc:generate": "pnpm build:declarations && pnpm typedoc:generate:skip-build", - "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && rm -rf .typedoc/docs && mv .typedoc/temp-docs .typedoc/docs", + "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && node .typedoc/extract-returns-and-params.mjs && rm -rf .typedoc/docs && mv .typedoc/temp-docs .typedoc/docs", "version-packages": "changeset version && pnpm install --lockfile-only --engine-strict=false", "version-packages:canary": "./scripts/canary.mjs", "version-packages:snapshot": "./scripts/snapshot.mjs", diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index 6e332da49bd..e5df2986156 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -18,10 +18,9 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid import { createGetToken, createSignOut } from './utils'; /** - * An object containing the initial authentication state or options for the `useAuth()` hook. If not provided, the hook will attempt to derive the state from the context. `treatPendingAsSignedOut` is a boolean that indicates whether pending sessions are considered as signed out or not. Defaults to `true`. - * @includeType initialAuthStateOrOptions + * @inline */ -export type UseAuthOptions = Record | PendingSessionOptions | undefined | null; +type UseAuthOptions = Record | PendingSessionOptions | undefined | null; /** * The `useAuth()` hook provides access to the current user's authentication state and methods to manage the active session. diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 34fd79a07bf..fcf84df542f 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -139,7 +139,6 @@ export type SignOutOptions = { /** * @inline - * @embedType */ export interface SignOut { (options?: SignOutOptions): Promise; @@ -1265,7 +1264,6 @@ export type SetActiveParams = { /** * @inline - * @embedType */ export type SetActive = (setActiveParams: SetActiveParams) => Promise; diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index eea77605524..65364875488 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -1,5 +1,6 @@ import type { SetActive, SignOut } from './clerk'; -import type { JwtPayload } from './jwtv2'; +import type { ActClaim, JwtPayload } from './jwtv2'; +import { OrganizationCustomRoleKey } from './organizationMembership'; import type { CheckAuthorizationWithCustomPermissions, GetToken, @@ -11,18 +12,15 @@ import type { SignUpResource } from './signUp'; import type { UserResource } from './user'; /** * @inline - * @embedType */ type CheckAuthorizationSignedOut = undefined; /** * @inline - * @embedType */ type CheckAuthorizationWithoutOrgOrUser = (params: Parameters[0]) => false; /** - * @unionReturnHeadings - * ["Initialization", "Signed out", "Signed in (no active organization)", "Signed in (with active organization)"] + * @inline */ export type UseAuthReturn = | { @@ -95,7 +93,7 @@ export type UseAuthReturn = userId: string; sessionId: string; sessionClaims: JwtPayload; - actor: { [x: string]: unknown; sub: string } | null; + actor: ActClaim | null; orgId: null; orgRole: null; orgSlug: null; @@ -109,9 +107,9 @@ export type UseAuthReturn = userId: string; sessionId: string; sessionClaims: JwtPayload; - actor: { [x: string]: unknown; sub: string } | null; + actor: ActClaim | null; orgId: string; - orgRole: string; + orgRole: OrganizationCustomRoleKey; orgSlug: string | null; has: CheckAuthorizationWithCustomPermissions; signOut: SignOut; diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 0db51f7ab2d..e041645bbe0 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -46,13 +46,11 @@ type DisallowSystemPermissions

= P extends `${OrganizationSyst /** * @inline - * @embedType */ export type CheckAuthorizationFn = (isAuthorizedParams: Params) => boolean; /** * @inline - * @embedType */ export type CheckAuthorizationWithCustomPermissions = CheckAuthorizationFn; @@ -351,7 +349,6 @@ export type GetTokenOptions = { }; /** * @inline - * @embedType */ export type GetToken = (options?: GetTokenOptions) => Promise; diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 87477eb70e0..d19227fc2e1 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -9,7 +9,6 @@ const CUSTOM_BLOCK_TAGS = [ '@paramExtension', '@experimental', '@hideReturns', - '@includeType', ]; /** @@ -110,7 +109,7 @@ const config = { excludeNotDocumented: true, gitRevision: 'main', blockTags: [...OptionDefaults.blockTags, ...CUSTOM_BLOCK_TAGS], - modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), '@embedType'], + modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental')], exclude: ['src/**/*.test.ts', 'src/**/*.test.tsx'], readme: 'none', disableGit: true, From bc7873cbbe25b3fd69d61cdad395fe86592d8b85 Mon Sep 17 00:00:00 2001 From: Nick Wylynko Date: Thu, 9 Oct 2025 01:10:29 +0800 Subject: [PATCH 14/18] Add new extract returns and params script --- packages/types/src/hooks.ts | 17 ++++++----------- packages/types/src/session.ts | 1 - 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 65364875488..6c8bc12b541 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -1,6 +1,6 @@ import type { SetActive, SignOut } from './clerk'; import type { ActClaim, JwtPayload } from './jwtv2'; -import { OrganizationCustomRoleKey } from './organizationMembership'; +import type { OrganizationCustomRoleKey } from './organizationMembership'; import type { CheckAuthorizationWithCustomPermissions, GetToken, @@ -117,8 +117,7 @@ export type UseAuthReturn = }; /** - * @unionReturnHeadings - * ["Initialization", "Loaded"] + * @inline */ export type UseSignInReturn = | { @@ -142,8 +141,7 @@ export type UseSignInReturn = }; /** - * @unionReturnHeadings - * ["Initialization", "Loaded"] + * @inline */ export type UseSignUpReturn = | { @@ -167,8 +165,7 @@ export type UseSignUpReturn = }; /** - * @unionReturnHeadings - * ["Initialization", "Signed out", "Signed in"] + * @inline */ export type UseSessionReturn = | { @@ -197,8 +194,7 @@ export type UseSessionReturn = }; /** - * @unionReturnHeadings - * ["Initialization", "Loaded"] + * @inline */ export type UseSessionListReturn = | { @@ -222,8 +218,7 @@ export type UseSessionListReturn = }; /** - * @unionReturnHeadings - * ["Initialization", "Signed out", "Signed in"] + * @inline */ export type UseUserReturn = | { diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index e041645bbe0..d873a48f739 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -30,7 +30,6 @@ import type { Autocomplete } from './utils'; /** * @inline - * @embedType */ export type PendingSessionOptions = { /** From 2ebb4f2c3937f143598c4c606cb7506cbfa6bc6b Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 8 Oct 2025 13:25:46 -0600 Subject: [PATCH 15/18] Remove unwanted changes --- packages/types/src/hooks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 6c8bc12b541..d460c75d027 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -10,6 +10,7 @@ import type { import type { SignInResource } from './signIn'; import type { SignUpResource } from './signUp'; import type { UserResource } from './user'; + /** * @inline */ From 906c42c0436f0c5506b9a17fd25c28e29efdf81e Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 9 Oct 2025 15:43:21 -0600 Subject: [PATCH 16/18] Billing hooks jsdoc work --- .typedoc/custom-plugin.mjs | 3 + packages/shared/src/react/commerce.tsx | 51 ++++++++++-- packages/shared/src/react/contexts.tsx | 6 ++ .../hooks/createBillingPaginatedHook.tsx | 16 +++- .../shared/src/react/hooks/useCheckout.ts | 78 +++++++++++++++---- .../src/react/hooks/usePaymentAttempts.tsx | 7 +- .../src/react/hooks/usePaymentMethods.tsx | 7 +- packages/shared/src/react/hooks/usePlans.tsx | 7 +- .../shared/src/react/hooks/useStatements.tsx | 7 +- .../src/react/hooks/useSubscription.tsx | 9 +-- packages/shared/typedoc.json | 2 +- 11 files changed, 159 insertions(+), 34 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 2f6422db630..c01e14d0c93 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -57,14 +57,17 @@ const LINK_REPLACEMENTS = [ ['verify-token-options', '#verify-token-options'], ['localization-resource', '/docs/guides/customizing-clerk/localization'], ['confirm-checkout-params', '/docs/reference/javascript/types/billing-checkout-resource#parameters'], + ['billing-payment-resource', '/docs/reference/javascript/types/billing-payment-resource'], ['billing-payment-source-resource', '/docs/reference/javascript/types/billing-payment-source-resource'], ['billing-payer-resource', '/docs/reference/javascript/types/billing-payer-resource'], ['billing-plan-resource', '/docs/reference/javascript/types/billing-plan-resource'], ['billing-checkout-totals', '/docs/reference/javascript/types/billing-checkout-totals'], + ['billing-checkout-resource', '/docs/reference/javascript/types/billing-checkout-resource'], ['billing-money-amount', '/docs/reference/javascript/types/billing-money-amount'], ['billing-subscription-item-resource', '/docs/reference/javascript/types/billing-subscription-item-resource'], ['feature-resource', '/docs/reference/javascript/types/feature-resource'], ['billing-statement-group', '/docs/reference/javascript/types/billing-statement-group'], + ['billing-subscription-resource', '/docs/reference/javascript/types/billing-subscription-resource'], ]; /** diff --git a/packages/shared/src/react/commerce.tsx b/packages/shared/src/react/commerce.tsx index 72fd978f4b1..3d77a1b6eeb 100644 --- a/packages/shared/src/react/commerce.tsx +++ b/packages/shared/src/react/commerce.tsx @@ -6,7 +6,6 @@ import useSWR from 'swr'; import useSWRMutation from 'swr/mutation'; import { createContextAndHook } from './hooks/createContextAndHook'; -import type { useCheckout } from './hooks/useCheckout'; import { useClerk } from './hooks/useClerk'; import { useOrganization } from './hooks/useOrganization'; import { useUser } from './hooks/useUser'; @@ -139,15 +138,27 @@ type internalStripeAppearance = { spacingUnit: string; }; -type PaymentElementProviderProps = { - checkout?: BillingCheckoutResource | ReturnType['checkout']; +/** + * @interface + */ +export type PaymentElementProviderProps = { + /** + * An optional checkout resource object. When provided, the payment element is scoped to the specific checkout session. + */ + checkout?: BillingCheckoutResource; + /** + * An optional object to customize the appearance of the Stripe Payment Element. This allows you to match the form's styling to your application's theme. + */ stripeAppearance?: internalStripeAppearance; /** - * Default to `user` if not provided. + * Specifies whether the payment method is being added for a user or an organization. * * @default 'user' */ for?: ForPayerType; + /** + * An optional description to display to the user within the payment element UI. + */ paymentDescription?: string; }; @@ -229,7 +240,17 @@ const PaymentElementInternalRoot = (props: PropsWithChildren) => { return {props.children}; }; -const PaymentElement = ({ fallback }: { fallback?: ReactNode }) => { +/** + * @interface + */ +export type PaymentElementProps = { + /** + * Optional fallback content, such as a loading skeleton, to display while the payment form is being initialized. + */ + fallback?: ReactNode; +}; + +const PaymentElement = ({ fallback }: PaymentElementProps) => { const { setIsPaymentElementReady, paymentMethodOrder, @@ -296,7 +317,13 @@ const throwLibsMissingError = () => { ); }; -type UsePaymentElementReturn = { +/** + * @interface + */ +export type UsePaymentElementReturn = { + /** + * A function that submits the payment form data to the payment provider. It returns a promise that resolves with either a `data` object containing a payment token on success, or an `error` object on failure. + */ submit: () => Promise< | { data: { gateway: 'stripe'; paymentToken: string }; @@ -307,13 +334,25 @@ type UsePaymentElementReturn = { error: PaymentElementError; } >; + /** + * A function that resets the payment form to its initial, empty state. + */ reset: () => Promise; + /** + * A boolean that indicates if the payment form UI has been rendered and is ready for user input. This is useful for disabling a submit button until the form is interactive. + */ isFormReady: boolean; } & ( | { + /** + * An object containing information about the initialized payment provider. It is `undefined` until `isProviderReady` is `true`. + */ provider: { name: 'stripe'; }; + /** + * A boolean that indicates if the underlying payment provider (e.g. Stripe) has been fully initialized. + */ isProviderReady: true; } | { diff --git a/packages/shared/src/react/contexts.tsx b/packages/shared/src/react/contexts.tsx index c54ea6f7a2a..31cfdf9a091 100644 --- a/packages/shared/src/react/contexts.tsx +++ b/packages/shared/src/react/contexts.tsx @@ -26,8 +26,14 @@ const [SessionContext, useSessionContext] = createContextAndHook({}); type UseCheckoutOptions = { + /** + */ for?: ForPayerType; + /** + */ planPeriod: BillingSubscriptionPlanPeriod; + /** + */ planId: string; }; diff --git a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx index 9d9bb6c2b28..bb8f69494c8 100644 --- a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx +++ b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx @@ -24,6 +24,18 @@ type BillingHookConfig & { + /** + * Specifies whether the payment method is being added for a user or an organization. + * + * @default 'user' + */ + for?: ForPayerType; +}; + /** * A hook factory that creates paginated data fetching hooks for commerce-related resources. * It provides a standardized way to create hooks that can fetch either user or organization resources @@ -44,10 +56,6 @@ export function createBillingPaginatedHook) { - type HookParams = PaginatedHookConfig & { - for?: ForPayerType; - }; - return function useBillingHook( params?: T, ): PaginatedResources { diff --git a/packages/shared/src/react/hooks/useCheckout.ts b/packages/shared/src/react/hooks/useCheckout.ts index 507553b0c6b..83ceccf470f 100644 --- a/packages/shared/src/react/hooks/useCheckout.ts +++ b/packages/shared/src/react/hooks/useCheckout.ts @@ -27,11 +27,23 @@ type ForceNull = { [K in keyof T]: null; }; +/** + * @inline + */ type CheckoutProperties = Omit, 'pathRoot' | 'status'>; +/** + * @inline + */ type FetchStatusAndError = | { + /** + * Returns an error object if any part of the checkout process fails. + */ error: ClerkAPIResponseError; + /** + * The data fetching status. + */ fetchStatus: 'error'; } | { @@ -40,34 +52,72 @@ type FetchStatusAndError = }; /** - * @internal + * @inline * On status === 'needs_initialization', all properties are null. * On status === 'needs_confirmation' or 'completed', all properties are defined the same as the CommerceCheckoutResource. */ type CheckoutPropertiesPerStatus = | ({ + /** + * @inline + * The current status of the checkout session. The following statuses are possible: + * - `needs_initialization`: The checkout hasn't started but the hook is mounted. Call start() to continue. + * - `needs_confirmation`: The checkout has been initialized and is awaiting confirmation. Call confirm() to continue. + * - `completed`: The checkout has been successfully confirmed. Call finalize() to complete the checkout. + */ status: Extract<__experimental_CheckoutCacheState['status'], 'needs_initialization'>; } & ForceNull) | ({ status: Extract<__experimental_CheckoutCacheState['status'], 'needs_confirmation' | 'completed'>; } & CheckoutProperties); -type __experimental_UseCheckoutReturn = { - checkout: FetchStatusAndError & - CheckoutPropertiesPerStatus & { - confirm: __experimental_CheckoutInstance['confirm']; - start: __experimental_CheckoutInstance['start']; - clear: () => void; - finalize: (params?: { navigate?: SetActiveNavigate }) => void; - getState: () => __experimental_CheckoutCacheState; - isStarting: boolean; - isConfirming: boolean; - }; +/** + * @interface + */ +export type UseCheckoutReturn = FetchStatusAndError & + CheckoutPropertiesPerStatus & { + /** + * A function that confirms and finalizes the checkout process, usually after the user has provided and validated payment information. + */ + confirm: __experimental_CheckoutInstance['confirm']; + /** + * A function that initializes the checkout process by creating a checkout resource on the server. + */ + start: __experimental_CheckoutInstance['start']; + /** + * A function that clears the current checkout state from the cache. + */ + clear: () => void; + /** + * A function that finalizes the checkout process. Can optionally accept a `redirectUrl` to navigate the user to upon completion. + */ + finalize: (params?: { navigate?: SetActiveNavigate }) => void; + getState: () => __experimental_CheckoutCacheState; + /** + * A boolean that indicates if the `start()` method is in progress. + */ + isStarting: boolean; + /** + * A boolean that indicates if the `confirm()` method is in progress. + */ + isConfirming: boolean; + }; + +/** + * @interface + */ +export type __experimental_UseCheckoutReturn = { + /** + */ + checkout: UseCheckoutReturn; }; -type Params = Parameters[0]; +/** + * @interface + */ +export type UseCheckoutParams = Parameters[0]; -export const useCheckout = (options?: Params): __experimental_UseCheckoutReturn => { +export const useCheckout = (options?: UseCheckoutParams): __experimental_UseCheckoutReturn => { const contextOptions = useCheckoutContext(); const { for: forOrganization, planId, planPeriod } = options || contextOptions; diff --git a/packages/shared/src/react/hooks/usePaymentAttempts.tsx b/packages/shared/src/react/hooks/usePaymentAttempts.tsx index f74ba4c09cc..3a3e4a587bc 100644 --- a/packages/shared/src/react/hooks/usePaymentAttempts.tsx +++ b/packages/shared/src/react/hooks/usePaymentAttempts.tsx @@ -4,7 +4,7 @@ import { useClerkInstanceContext } from '../contexts'; import { createBillingPaginatedHook } from './createBillingPaginatedHook'; /** - * @internal + * */ export const usePaymentAttempts = createBillingPaginatedHook({ hookName: 'usePaymentAttempts', @@ -17,3 +17,8 @@ export const usePaymentAttempts = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/usePaymentMethods.tsx b/packages/shared/src/react/hooks/usePaymentMethods.tsx index 8537f32caa9..a6f63b848fc 100644 --- a/packages/shared/src/react/hooks/usePaymentMethods.tsx +++ b/packages/shared/src/react/hooks/usePaymentMethods.tsx @@ -4,7 +4,7 @@ import { useOrganizationContext, useUserContext } from '../contexts'; import { createBillingPaginatedHook } from './createBillingPaginatedHook'; /** - * @internal + * */ export const usePaymentMethods = createBillingPaginatedHook({ hookName: 'usePaymentMethods', @@ -19,3 +19,8 @@ export const usePaymentMethods = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/usePlans.tsx b/packages/shared/src/react/hooks/usePlans.tsx index 77a45213908..950e68a2c02 100644 --- a/packages/shared/src/react/hooks/usePlans.tsx +++ b/packages/shared/src/react/hooks/usePlans.tsx @@ -4,7 +4,7 @@ import { useClerkInstanceContext } from '../contexts'; import { createBillingPaginatedHook } from './createBillingPaginatedHook'; /** - * @internal + * */ export const usePlans = createBillingPaginatedHook({ hookName: 'usePlans', @@ -23,3 +23,8 @@ export const usePlans = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/useStatements.tsx b/packages/shared/src/react/hooks/useStatements.tsx index 78817ffebc1..3e99df960d1 100644 --- a/packages/shared/src/react/hooks/useStatements.tsx +++ b/packages/shared/src/react/hooks/useStatements.tsx @@ -4,7 +4,7 @@ import { useClerkInstanceContext } from '../contexts'; import { createBillingPaginatedHook } from './createBillingPaginatedHook'; /** - * @internal + * */ export const useStatements = createBillingPaginatedHook({ hookName: 'useStatements', @@ -17,3 +17,8 @@ export const useStatements = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/useSubscription.tsx b/packages/shared/src/react/hooks/useSubscription.tsx index a2197ebf0f5..1ead451d264 100644 --- a/packages/shared/src/react/hooks/useSubscription.tsx +++ b/packages/shared/src/react/hooks/useSubscription.tsx @@ -36,13 +36,13 @@ export type UseSubscriptionParams = { export type UseSubscriptionReturn = | { /** - * A boolean that indicates whether the initial data is still being fetched. + * The subscription object, or `null` if the data hasn't been loaded yet. */ data: null; /** * A boolean that indicates whether the initial data is still being fetched. */ - isLoaded: false; + isLoading: false; /** * A boolean that indicates whether any request is still in flight, including background updates. */ @@ -58,21 +58,20 @@ export type UseSubscriptionReturn = } | { data: BillingSubscriptionResource; - isLoaded: true; + isLoading: true; isFetching: true; error: Error; revalidate: () => Promise; } | { data: BillingSubscriptionResource | null; - isLoaded: boolean; + isLoading: boolean; isFetching: boolean; error: Error | null; revalidate: () => Promise; }; /** - * @internal * * Fetches subscription data for the current user or organization. * diff --git a/packages/shared/typedoc.json b/packages/shared/typedoc.json index 332cb7df315..1967050b121 100644 --- a/packages/shared/typedoc.json +++ b/packages/shared/typedoc.json @@ -1,6 +1,6 @@ { "$schema": "https://typedoc.org/schema.json", - "entryPoints": ["./src/index.ts", "./src/react/types.ts", "./src/react/hooks/*.{ts,tsx}"], + "entryPoints": ["./src/index.ts", "./src/react/types.ts", "./src/react/hooks/*.{ts,tsx}", "./src/react/commerce.tsx"], "compilerOptions": { "noImplicitAny": false } From 113bb22826d899a5e06534200592465308cd64e5 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 9 Oct 2025 19:28:50 -0600 Subject: [PATCH 17/18] Refine useCheckout --- .typedoc/custom-plugin.mjs | 8 ++++++++ packages/shared/src/react/contexts.tsx | 5 +++++ packages/shared/src/react/hooks/useCheckout.ts | 8 +++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index c01e14d0c93..2490a738f9d 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -19,6 +19,14 @@ const FILES_WITHOUT_HEADINGS = [ 'verify-token-options.mdx', 'public-organization-data-json.mdx', 'organization-membership-public-user-data.mdx', + 'use-checkout-return.mdx', + 'use-checkout-params.mdx', + 'use-payment-element-return.mdx', + 'use-payment-methods-return.mdx', + 'use-payment-attempts-return.mdx', + 'use-plans-return.mdx', + 'use-statements-return.mdx', + 'use-subscription-return.mdx', ]; /** diff --git a/packages/shared/src/react/contexts.tsx b/packages/shared/src/react/contexts.tsx index 31cfdf9a091..56b2d6c2f41 100644 --- a/packages/shared/src/react/contexts.tsx +++ b/packages/shared/src/react/contexts.tsx @@ -27,12 +27,17 @@ const OptionsContext = React.createContext({}); type UseCheckoutOptions = { /** + * Specifies if the checkout is for an organization. + * + * @default 'user' */ for?: ForPayerType; /** + * The billing period for the plan. */ planPeriod: BillingSubscriptionPlanPeriod; /** + * The ID of the subscription plan to check out (e.g. `cplan_xxx`). */ planId: string; }; diff --git a/packages/shared/src/react/hooks/useCheckout.ts b/packages/shared/src/react/hooks/useCheckout.ts index 83ceccf470f..0d7675b1322 100644 --- a/packages/shared/src/react/hooks/useCheckout.ts +++ b/packages/shared/src/react/hooks/useCheckout.ts @@ -61,9 +61,11 @@ type CheckoutPropertiesPerStatus = /** * @inline * The current status of the checkout session. The following statuses are possible: - * - `needs_initialization`: The checkout hasn't started but the hook is mounted. Call start() to continue. - * - `needs_confirmation`: The checkout has been initialized and is awaiting confirmation. Call confirm() to continue. - * - `completed`: The checkout has been successfully confirmed. Call finalize() to complete the checkout. + *

    + *
  • `needs_initialization`: The checkout hasn't started but the hook is mounted. Call `start()` to continue.
  • + *
  • `needs_confirmation`: The checkout has been initialized and is awaiting confirmation. Call `confirm()` to continue.
  • + *
  • `completed`: The checkout has been successfully confirmed. Call `finalize()` to complete the checkout.
  • + *
*/ status: Extract<__experimental_CheckoutCacheState['status'], 'needs_initialization'>; } & ForceNull) From d58bcbd07aea2fceab97e02606bac1e82ece56ac Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 9 Oct 2025 19:39:08 -0600 Subject: [PATCH 18/18] Add billing hooks params to files without headings --- .typedoc/custom-plugin.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 2490a738f9d..abefe70aeb9 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -22,10 +22,13 @@ const FILES_WITHOUT_HEADINGS = [ 'use-checkout-return.mdx', 'use-checkout-params.mdx', 'use-payment-element-return.mdx', + 'use-payment-element-return.mdx', 'use-payment-methods-return.mdx', 'use-payment-attempts-return.mdx', 'use-plans-return.mdx', 'use-statements-return.mdx', + 'hook-params.mdx', + 'use-subscription-params.mdx', 'use-subscription-return.mdx', ];