> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# React Quickstart (JS SDK)

Use the JavaScript SDK inside a React app. State is wired via the `@dynamic-labs-sdk/react-hooks` package.

> [!WARNING]
>
> The JavaScript SDK is headless: there is no built-in modal or login form, and
> you build most UI yourself. For React web apps,
> [React UI components](https://docs.dynamic.xyz/docs/javascript/building-ui/react-ui/overview.md) provide
> prebuilt widgets for wallet connection and Flow payments. See
> [Building UI](https://docs.dynamic.xyz/docs/javascript/building-ui/overview.md) for everything else.

> [!NOTE]
>
> Before you start: a Vite-based React app and a Dynamic environment ID from the [Dynamic Console](https://console.dynamic.xyz). Using Next.js App Router instead of Vite? See [Next.js App Router setup](https://docs.dynamic.xyz/docs/javascript/reference/nextjs-app-router.md).

> [tab] Agent-friendly
>
> > [!TIP]
> >
> > Add the Dynamic docs MCP to your AI editor first — your agent can then query the docs directly.
>
> > [tab] Cursor
> >
> > [Click here to add the MCP to Cursor.](https://cursor.com/en/install-mcp?name=dynamic\&config=eyJ1cmwiOiJodHRwczovL3d3dy5keW5hbWljLnh5ei9kb2NzL21jcCJ9)
>
> > [tab] Claude Code
> >
> > ```bash theme={"system"}
> > claude mcp add --transport http dynamic https://www.dynamic.xyz/docs/mcp
> > ```
>
> > [tab] Codex
> >
> > ```bash theme={"system"}
> > codex mcp add dynamic --url https://www.dynamic.xyz/docs/mcp
> > ```
>
> > [tab] Other
> >
> > If your editor or agent supports remote HTTP MCP, add a server that points at `https://www.dynamic.xyz/docs/mcp`. The exact steps depend on the product; use its MCP documentation for URL-based or HTTP transport setup.
>
> Copy this prompt into your LLM (e.g. Claude, ChatGPT, Cursor) to implement Dynamic in your React app via the JS SDK.
>
> ````markdown theme={"system"}
> # React SDK Quickstart
>
> ## Default for React apps
>
> **This is the recommended quickstart for React.** Use this document, not the React SDK quickstart (`/react/reference/quickstart`, agent prompt id: `react`), when integrating Dynamic into a new or existing React app unless the user already uses `@dynamic-labs/sdk-react-core` or explicitly requests the old React SDK.
>
> ## Purpose and Usage
>
> This document provides structured quickstart information for using the Dynamic JavaScript SDK inside a React app. It is designed to be consumed by LLMs to generate accurate installation commands and code examples based on user requirements.
>
> Key fact: state flows through `@dynamic-labs-sdk/react-hooks`.
>
> **How to use this document:**
>
> Start by asking the user which path they want:
>
> > "Would you like a **Quick start** (email login + EVM + embedded wallet, zero configuration; recommended if you're just getting started) or a **Custom setup** (choose your chains, auth method, and wallet type)?"
>
> - If the user chooses **Quick start**: follow the Quick Start Path section exactly. Do not ask any configuration questions. Execute the happy path as written.
> - If the user chooses **Custom setup**: follow the Custom Setup Path section and ask all configuration questions before proceeding.
>
> ---
>
> ## Quick Start Path
>
> > Use this path when the user chooses Quick start. Execute every step in order. Do not skip steps. Do not ask configuration questions.
>
> **Stack:** Vite + React 18 + npm + EVM only + email OTP + embedded WaaS wallet + `@dynamic-labs-sdk/react-hooks`
>
> ### Step 1: Prerequisites
>
> Tell the user:
> - Node.js 18+ is required
> - A Vite + React project is required (e.g. `npm create vite@latest my-app -- --template react-ts`)
> - A Dynamic environment ID is required. Get one at https://console.dynamic.xyz/dashboard/developer/api
>
> Ask the user for their environment ID before proceeding.
>
> ### Step 2: Install
>
> ```
> npm i @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/react-hooks @tanstack/react-query viem
> ```
>
> `@tanstack/react-query` is a required peer dependency of `@dynamic-labs-sdk/react-hooks`. Every state, query, and mutation hook is built on TanStack Query. `viem` is a required peer dependency of `@dynamic-labs-sdk/evm`.
>
> ### Step 3: Create the Dynamic client module
>
> Create `src/dynamicClient.ts`. Importing this file at app root registers extensions before any component renders.
>
> ```typescript
> import { createDynamicClient } from "@dynamic-labs-sdk/client";
> import { addEvmExtension } from "@dynamic-labs-sdk/evm";
>
> export const dynamicClient = createDynamicClient({
>   environmentId: "YOUR_ENVIRONMENT_ID",
>   metadata: {
>     name: "My App",
>     // IMPORTANT: the property is `universalLink`, not `url`
>     universalLink: window.location.origin,
>   },
> });
>
> // Register extensions immediately after createDynamicClient().
> // The client argument is optional: omit it unless you run multiple clients.
> addEvmExtension();
> ```
>
> ### Step 4: Wrap the app in `QueryClientProvider` and `DynamicProvider`
>
> Update `src/main.tsx` to import the client module (registering extensions) and wrap the tree. `DynamicProvider` supplies the Dynamic client context; `QueryClientProvider` is required because every hook in `@dynamic-labs-sdk/react-hooks` uses TanStack Query internally. Mount `QueryClientProvider` outside `DynamicProvider`:
>
> ```tsx
> import { StrictMode } from "react";
> import { createRoot } from "react-dom/client";
> import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
> import { DynamicProvider } from "@dynamic-labs-sdk/react-hooks";
>
> import { App } from "./App";
> import { dynamicClient } from "./dynamicClient";
>
> const queryClient = new QueryClient();
>
> createRoot(document.getElementById("root")!).render(
>   <StrictMode>
>     <QueryClientProvider client={queryClient}>
>       <DynamicProvider client={dynamicClient}>
>         <App />
>       </DynamicProvider>
>     </QueryClientProvider>
>   </StrictMode>
> );
> ```
>
> ### Step 5: Email OTP login component
>
> Build a minimal login form. The JS SDK is headless: there is no built-in modal.
>
> ```tsx
> import { useState } from "react";
> import { useSendEmailOTP, useVerifyOTP } from "@dynamic-labs-sdk/react-hooks";
>
> export function Login() {
>   const [email, setEmail] = useState("");
>   const [code, setCode] = useState("");
>
>   const { mutate: sendEmailOTP, data: otpVerification } = useSendEmailOTP();
>   const { mutate: verifyOTP } = useVerifyOTP();
>
>   if (!otpVerification) {
>     return (
>       <>
>         <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="email" />
>         <button onClick={() => sendEmailOTP({ email })}>Send code</button>
>       </>
>     );
>   }
>
>   return (
>     <>
>       <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="123456" />
>       <button onClick={() => verifyOTP({ otpVerification, verificationToken: code })}>
>         Verify
>       </button>
>     </>
>   );
> }
> ```
>
> ### Step 6: Create the WaaS wallet after auth (required)
>
> **After `verifyOTP` succeeds, the wallet does not exist yet.** Subscribe once at app mount with `useOnEvent` and call `createWaasWalletAccounts()` unconditionally on `userChanged`. Do not guard this with an `accounts.length === 0` check. The SDK may return a stale non-zero list immediately after auth, causing the creation step to be silently skipped.
>
> ```tsx
> import { useOnEvent } from "@dynamic-labs-sdk/react-hooks";
> // WaaS functions are exported from the /waas subpath
> import { createWaasWalletAccounts, getChainsMissingWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas";
>
> export function WaasBootstrap() {
>   useOnEvent({
>     event: "userChanged",
>     listener: async ({ user }) => {
>       if (!user) return;
>       const missingChains = getChainsMissingWaasWalletAccounts();
>       if (missingChains.length === 0) return;
>       await createWaasWalletAccounts({ chains: missingChains });
>     },
>   });
>   return null;
> }
> ```
>
> Mount `<WaasBootstrap />` once inside `DynamicProvider`.
>
> ### Step 7: Display wallet state with hooks
>
> Hooks from `@dynamic-labs-sdk/react-hooks` subscribe to client events and re-render automatically. **Use `wallet.address`, not `wallet.accountAddress`.**
>
> ```tsx
> import { useUser, useGetWalletAccounts, useInitStatus } from "@dynamic-labs-sdk/react-hooks";
>
> export function Dashboard() {
>   const { data: initStatus } = useInitStatus();
>   const { data: user } = useUser();
>   const { data: accounts = [] } = useGetWalletAccounts();
>
>   if (initStatus !== "finished") return <p>Loading…</p>;
>   if (!user) return <p>Not signed in</p>;
>
>   const address = accounts[0]?.address;
>   return (
>     <>
>       <p>Signed in as {user.email}</p>
>       <p>Wallet: {address}</p>
>     </>
>   );
> }
> ```
>
> ### Step 8: Logout
>
> ```tsx
> import { useLogout } from "@dynamic-labs-sdk/react-hooks";
>
> export function LogoutButton() {
>   const { mutate: logout } = useLogout();
>   return <button onClick={() => logout()}>Log out</button>;
> }
> ```
>
> ---
>
> ## Custom Setup Path
>
> > Use this path when the user chooses Custom setup. Ask ALL questions below before generating any code.
>
> **Questions to ask the user:**
>
> 1. Which package manager do you prefer? (npm, yarn, pnpm, bun)
> 2. Which chains do you want to support? (EVM, Solana, Sui, Aptos, Bitcoin, Tron, Starknet, TON, Cosmos, Aleo, Stellar; one or more)
> 3. If EVM or Solana: do you need only embedded wallets (smaller bundle) or the full extension including external wallet discovery?
> 4. If EVM or Solana: do you need WalletConnect for cross-device connections (QR code / deep link)?
> 5. Which auth method? (email OTP, SMS OTP, social, external wallet, or a combination)
>
> **Only after receiving answers**, use the sections below to generate the correct setup. Always include `@dynamic-labs-sdk/react-hooks` regardless of chain selection.
>
> ### Package Manager Commands
> - `npm`: `npm i`
> - `yarn`: `yarn add`
> - `pnpm`: `pnpm add`
> - `bun`: `bun add`
>
> ### Package Mapping
> - Core (always required): `@dynamic-labs-sdk/client`
> - React state hooks (always required for React apps): `@dynamic-labs-sdk/react-hooks` and `@tanstack/react-query` (peer dependency)
> - Prebuilt UI (optional, React web apps only): `@dynamic-labs-sdk/react-ui` for the connect wallet widget and the Flow payment widget. See https://docs.dynamic.xyz/javascript/building-ui/react-ui/overview
> - EVM: `@dynamic-labs-sdk/evm` plus `viem` (peer dependency)
> - Solana: `@dynamic-labs-sdk/solana`
> - Sui: `@dynamic-labs-sdk/sui`
> - Aptos: `@dynamic-labs-sdk/aptos`
> - Bitcoin: `@dynamic-labs-sdk/bitcoin`
> - Tron: `@dynamic-labs-sdk/tron`
> - Starknet: `@dynamic-labs-sdk/starknet`
> - TON: `@dynamic-labs-sdk/ton` plus `@ton/core`, `@ton/crypto`, `@ton/ton`, `@tonconnect/sdk`, `@tonconnect/ui` (peer dependencies)
> - Cosmos: `@dynamic-labs-sdk/cosmos`
> - Aleo: `@dynamic-labs-sdk/aleo` (no default extension: use `addWaasAleoExtension` from `@dynamic-labs-sdk/aleo/waas` for embedded wallets, or `addAleoWalletStandardExtension` from `@dynamic-labs-sdk/aleo/walletStandard` for external wallets)
> - Stellar: `@dynamic-labs-sdk/stellar`
> - WalletConnect (EVM/Solana only, optional): use `addWalletConnectEvmExtension` from `@dynamic-labs-sdk/evm/wallet-connect`, `addWalletConnectSolanaExtension` from `@dynamic-labs-sdk/solana/wallet-connect`. See [WalletConnect Integration](/javascript/reference/wallets/walletconnect-integration).
>
> ### Extension Notes
> - **Default extensions** (`addEvmExtension`, `addSolanaExtension`, `addTonExtension`, `addSuiExtension`, `addTronExtension`, `addBitcoinExtension`) bundle external wallet discovery + embedded (WaaS) support
> - **Standalone embedded-only extensions** (`addWaasEvmExtension`, `addWaasSolanaExtension`, `addWaasTonExtension`, `addWaasSuiExtension`, `addWaasTronExtension`, `addWaasBitcoinExtension`, `addWaasAleoExtension`) produce a smaller bundle. Use them when the user only needs embedded wallets. They live on the chain package's `/waas` subpath, for example `import { addWaasEvmExtension } from '@dynamic-labs-sdk/evm/waas';`
> - Every extension and SDK function takes an **optional trailing client argument** that defaults to the client created by `createDynamicClient()`. Omit it unless the app runs multiple clients
> - Register extensions immediately after `createDynamicClient()`, before initialization completes
> - See [Adding EVM Extensions](/javascript/reference/evm/adding-evm-extensions), [Adding Solana Extensions](/javascript/reference/solana/adding-solana-extensions), and [Adding TON Extensions](/javascript/reference/ton/adding-ton-extension) for the full list of standalone options
>
> ### React Wiring (apply to all custom setups)
>
> Always do these in any React + JS SDK app:
>
> 1. Create the client and register extensions in a single module (`dynamicClient.ts`). Import this module at app root so extensions are registered before any component renders.
> 2. Install `@tanstack/react-query` alongside `@dynamic-labs-sdk/react-hooks`. It is a required peer dependency.
> 3. Wrap the React tree in `<QueryClientProvider client={queryClient}>` (outer) and `<DynamicProvider client={dynamicClient}>` (inner) from `@dynamic-labs-sdk/react-hooks`.
> 4. Read state with hooks from `@dynamic-labs-sdk/react-hooks`: `useUser`, `useGetWalletAccounts`, `useGetAvailableWalletProvidersData`, `useInitStatus`, `useSessionExpiresAt`, `useGetUserSocialAccounts`, `useDynamicClient`. They auto-subscribe and trigger re-renders.
> 5. For one-off side-effect listeners (e.g. WaaS bootstrap on `userChanged`), use `useOnEvent`. Do not call `onEvent` directly inside components. It will leak subscriptions across re-renders.
>
> ### Post-Auth Patterns (apply to all custom setups that use embedded wallets)
>
> These steps are **required** and are not in the client init. They must be added to your post-auth flow:
>
> - **WaaS wallet creation**: not automatic. Trigger from a `useOnEvent` on `userChanged`:
>   ```tsx
>   import { useOnEvent } from "@dynamic-labs-sdk/react-hooks";
>   import { getChainsMissingWaasWalletAccounts, createWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas";
>
>   useOnEvent({
>     event: "userChanged",
>     listener: async ({ user }) => {
>       if (!user) return;
>       const missingChains = getChainsMissingWaasWalletAccounts();
>       if (missingChains.length > 0) {
>         await createWaasWalletAccounts({ chains: missingChains });
>       }
>     },
>   });
>   ```
> - **State hooks return the react-query result**: read the value off `data`, e.g. `const { data: accounts = [] } = useGetWalletAccounts()` then `accounts[0]?.address` (not `useGetWalletAccounts()[0]` and not `accountAddress`)
> - **OTP verification**: parameter is `verificationToken`, not `otp`
> - **Client metadata**: property is `universalLink`, not `url`
>
> ### Valid Combinations
> - Any single chain, or any combination of two or more chains
> - At least one chain must be selected
> - `@dynamic-labs-sdk/react-hooks` is included in every React combination
>
> ### Documentation
> All docs for this SDK: https://docs.dynamic.xyz (paths starting with `/javascript/`)
> Environment ID: https://console.dynamic.xyz/dashboard/developer/api
>
> ---
>
> ## Critical API Reference (apply to both paths)
>
> | Correct | Incorrect | Notes |
> |---|---|---|
> | `metadata: { universalLink: window.location.origin }` | `metadata: { url: window.location.origin }` | Renamed in v0.24+ |
> | `<QueryClientProvider client={queryClient}>…</QueryClientProvider>` | Using hooks without TanStack Query | Throws `No QueryClient set, use QueryClientProvider to set one` on the first hook call |
> | `<DynamicProvider client={dynamicClient}>…</DynamicProvider>` | Importing hooks without a provider | `useUser`, `useGetWalletAccounts`, etc. require `DynamicProvider` in the tree |
> | `useOnEvent({ event, listener })` inside a component | `onEvent({ event, listener }, client)` inside a component | `useOnEvent` cleans up on unmount; `onEvent` will leak |
> | `addEvmExtension()` | `addEvmExtension(myClient)` when there is one client | The client argument is optional and defaults to the client you created |
> | `verifyOTP({ otpVerification, verificationToken: '123456' })` | `verifyOTP({ otpVerification, otp: '123456' })` | Parameter is `verificationToken` not `otp` |
> | `wallet.address` | `wallet.accountAddress` | Use `address` on objects from `useGetWalletAccounts()` |
> | Call `createWaasWalletAccounts()` unconditionally on `userChanged` | Guard with `accounts.length === 0` | Stale list may cause silent skip |
> | Import `dynamicClient.ts` at app root (e.g. `main.tsx`) | Import only inside components | Extensions must register before any component renders |
>
> ---
>
> ## React Hooks Cheat Sheet
>
> All from `@dynamic-labs-sdk/react-hooks`. Each hook subscribes internally and triggers a re-render on change, so no manual event wiring is needed. State hooks return the full react-query result (`{ data, isLoading, error, refetch, ... }`); the **`data`** column below is the type of `data`, which you destructure with an alias, e.g. `const { data: user } = useUser()`.
>
> | Hook | `data` | Re-renders on |
> |---|---|---|
> | `useInitStatus()` | `'uninitialized' \| 'in-progress' \| 'finished' \| 'failed'` | `initStatusChanged` |
> | `useUser()` | The current `User` object or `null` | `userChanged` |
> | `useGetWalletAccounts()` | `WalletAccount[]` | `walletAccountsChanged` |
> | `useGetAvailableWalletProvidersData()` | Available wallet providers list | `walletProviderChanged` |
> | `useSessionExpiresAt()` | Session expiry timestamp | `userChanged` |
> | `useGetUserSocialAccounts()` | Linked social accounts | `userChanged` |
> | `useDynamicClient()` | Returns the client directly (`const client = useDynamicClient()`) | Never (stable reference) |
> | `useOnEvent({ event, listener })` | No return value; subscribes a listener | Fires `listener` on each event; cleans up on unmount |
>
> ---
>
> ## Building a headless integration?
>
> The JavaScript SDK is always headless. See the [Authentication screens](/javascript/authentication-methods/headless-authentication) for a full list of screens your app needs to handle.
>
> ---
>
> ## Step-Up Authentication
>
> **Required before accepting the `2026_04_01` API version**: verify your
> minimum API version in [Console > Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api).
>
> The JavaScript SDK is always headless: there is no built-in step-up UI.
> **You must always handle step-up authentication manually:** check
> requirements, call the appropriate verification method, and wait for the
> elevated token before proceeding with the sensitive operation.
>
> See [Step-up authentication](/javascript/authentication-methods/step-up-auth/overview) for the full implementation guide.
>
> ---
>
> ## Device Registration
>
> **Required before accepting the `2026_04_01` API version**: verify your
> minimum API version in [Console > Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api).
>
> The JavaScript SDK is always headless: there is no built-in device
> registration UI. **You must always handle device registration manually:**
> check whether the current device needs registration after auth, detect and
> process the email verification redirect, and listen for completion events.
>
> Full guide: https://docs.dynamic.xyz/javascript/authentication-methods/device-registration
>
> ---
>
> ## Troubleshooting: Console Configuration
>
> If the app builds successfully but login fails, wallets don't appear, or you see network/auth errors, the most common causes are Dynamic Console settings that haven't been configured. Ask the user to verify each of the following in their Dynamic Console at https://console.dynamic.xyz:
>
> ### 1. Chains not enabled
> The EVM chain (or any other chain used in the quickstart) must be enabled under **Chains & Networks** in the console. If the chain isn't toggled on, wallet creation and signing will silently fail or return empty results.
>
> ### 2. Login method not enabled
> Email OTP (or whichever login method the app uses) must be toggled on under **Sign-in Methods**. If it isn't enabled, the auth flow will fail at the point of sending the OTP.
>
> ### 3. Embedded wallets not enabled
> If the app uses WaaS embedded wallets, the **Embedded Wallets** feature must be enabled under **Wallets** in the console. Without it, `createWaasWalletAccounts()` will return an error or produce no wallet.
>
> ### 4. CORS origin not allowlisted
> The URL the app is running on (e.g. `http://localhost:5173`) must be added to the **Allowed Origins** list in the console under **Security**. Without it, all SDK requests will be blocked by CORS. Add the exact origin including port.
>
> If all four are configured and the app is still not working, check the browser console for error codes and refer to https://docs.dynamic.xyz/overview/troubleshooting/general.
>
> ---
>
> ## React-Specific Pitfalls
>
> - **Hooks return `null`/empty before init completes.** Gate UI on `useInitStatus().data === 'finished'`.
> - **`DynamicProvider` must wrap the entire tree that uses hooks.** Mounting it inside a route or conditional will throw "useDynamicClient must be used within a DynamicProvider" inside any consumer outside that subtree.
> - **`DynamicProvider` does not include `QueryClientProvider`.** Mount both; put `QueryClientProvider` outside `DynamicProvider`.
> - **Do not call `onEvent` inside component bodies.** Use `useOnEvent` from `@dynamic-labs-sdk/react-hooks`, which deduplicates subscriptions and cleans up on unmount.
> - **Strict Mode double-invokes effects in development.** `useOnEvent` handles this correctly; raw `useEffect(() => onEvent(...), [])` will register two listeners under Strict Mode.
> - **Don't recreate `dynamicClient` per render.** It must live in a module-level singleton (e.g. `src/dynamicClient.ts`), never in component state.
> ````

> [tab] Manual
>
> [https://documentation-quickstarts.vercel.app/javascript-react?color-scheme=dark](https://documentation-quickstarts.vercel.app/javascript-react?color-scheme=dark)
>
> [https://documentation-quickstarts.vercel.app/javascript-react?color-scheme=light](https://documentation-quickstarts.vercel.app/javascript-react?color-scheme=light)
>
> > [!TIP]
> >
> > **After the embed**
> >
> > * Full client setup: [Creating a Dynamic Client](https://docs.dynamic.xyz/docs/javascript/reference/client/create-dynamic-client.md), [Initializing the Dynamic Client](https://docs.dynamic.xyz/docs/javascript/reference/client/initialize-dynamic-client.md).
> > * Add chains with [EVM](https://docs.dynamic.xyz/docs/javascript/reference/evm/adding-evm-extensions.md), [Solana](https://docs.dynamic.xyz/docs/javascript/reference/solana/adding-solana-extensions.md), or [all extension types](https://docs.dynamic.xyz/docs/javascript/reference/adding-extensions.md).
> > * Reactive React patterns: [React Hooks](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks.md), [onEvent](https://docs.dynamic.xyz/docs/javascript/reference/client/on-event.md).
> > * Something wrong? See [Troubleshooting](https://docs.dynamic.xyz/docs/get-started/troubleshooting/general.md).
>
> ## Key patterns
>
> The JS SDK is framework-agnostic. In React, use the hooks from `@dynamic-labs-sdk/react-hooks` to subscribe to SDK state: they handle event subscriptions internally and trigger re-renders automatically:
>
> ```tsx theme={"system"}
> import { useGetWalletAccounts, useUser, useInitStatus } from '@dynamic-labs-sdk/react-hooks';
>
> function Dashboard() {
>   const { data: walletAccounts = [] } = useGetWalletAccounts();
>   const { data: user } = useUser();
>   const { data: initStatus } = useInitStatus();
>   // re-renders automatically when wallets, user, or init status change
> }
> ```
>
> For one-off side-effect listeners, use `useOnEvent`:
>
> ```tsx theme={"system"}
> import { useOnEvent } from '@dynamic-labs-sdk/react-hooks';
>
> function LogoutLogger() {
>   useOnEvent({ event: 'logout', listener: () => console.log('User logged out') });
>   return null;
> }
> ```
>
> > [!NOTE]
> >
> > Import your `dynamicClient.ts` module at the root of your app (e.g. in `main.tsx`) so extensions are registered before any component renders.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.