> ## 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.

# Next.js App Router setup

> Use the JavaScript SDK in a Next.js App Router app: client boundaries, providers, and SSR.

Next.js App Router renders Server Components by default. The JavaScript SDK runs in the browser, so you create the Dynamic client in a module, wrap the tree from a `"use client"` provider, and keep SDK hooks in client components.

> [!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 this: a Next.js App Router app, a Dynamic environment ID from the [Dynamic Console](https://console.dynamic.xyz), and the packages from the [React Quickstart (JS SDK)](https://docs.dynamic.xyz/docs/javascript/reference/react-quickstart.md) (`@dynamic-labs-sdk/client`, `@dynamic-labs-sdk/evm`, `@dynamic-labs-sdk/react-hooks`, `@tanstack/react-query`).

## 1. Create the Dynamic client module

Create the client once in a module you import from client components. Register extensions immediately after `createDynamicClient`, before initialization completes. Extension functions take no arguments. Do not pass the client instance.

Use `universalLink` in metadata, not `url`. Guard `window` so the module can evaluate during SSR without throwing.

```ts app/lib/dynamicClient.ts theme={"system"}
import { createDynamicClient, initializeClient } from '@dynamic-labs-sdk/client';
import { addEvmExtension } from '@dynamic-labs-sdk/evm';

const environmentId = process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID;

if (!environmentId) {
  throw new Error('NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID is not set');
}

export const dynamicClient = createDynamicClient({
  autoInitialize: false,
  environmentId,
  metadata: {
    name: 'My App',
    universalLink:
      typeof window !== 'undefined'
        ? window.location.origin
        : 'http://localhost:3000',
  },
});

addEvmExtension();

if (typeof window !== 'undefined') {
  void initializeClient();
}
```

Set `NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID` in `.env.local`. Add your app origin to **Allowed Origins** in the console.

For other chains, see [Adding extensions](https://docs.dynamic.xyz/docs/javascript/reference/adding-extensions.md). For client options, see [Creating a Dynamic Client](https://docs.dynamic.xyz/docs/javascript/reference/client/create-dynamic-client.md) and [Initializing the Dynamic Client](https://docs.dynamic.xyz/docs/javascript/reference/client/initialize-dynamic-client.md).

## 2. Wrap the tree in client providers

`layout.tsx` can stay a Server Component. Providers that hold React state must live in a `"use client"` file.

Mount `QueryClientProvider` outside `DynamicProvider`. Create the `QueryClient` with `useState` so it is not shared across server requests.

> [tab] React hooks
>
> ```tsx app/components/providers.tsx theme={"system"}
> 'use client';
>
> import { useState } from 'react';
> import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
> import { DynamicProvider } from '@dynamic-labs-sdk/react-hooks';
> import { dynamicClient } from '@/lib/dynamicClient';
>
> export function Providers({ children }: { children: React.ReactNode }) {
>   const [queryClient] = useState(() => new QueryClient());
>
>   return (
>     <QueryClientProvider client={queryClient}>
>       <DynamicProvider client={dynamicClient}>{children}</DynamicProvider>
>     </QueryClientProvider>
>   );
> }
> ```

> [tab] Client functions only
>
> If you call `@dynamic-labs-sdk/client` functions directly and do not use hooks, you still need a client boundary so the module runs in the browser. You do not need `DynamicProvider` or `QueryClientProvider`.
>
> ```tsx app/components/providers.tsx theme={"system"}
> 'use client';
>
> import '@/lib/dynamicClient';
>
> export function Providers({ children }: { children: React.ReactNode }) {
>   return children;
> }
> ```

Import the wrapper from the root layout:

```tsx app/layout.tsx theme={"system"}
import { Providers } from './components/providers';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

Do not import `dynamicClient.ts` from a Server Component. That evaluates the SDK on the server.

## 3. Use the SDK in client components

Any file that calls SDK hooks or client functions that need the browser must start with `"use client"`. Gate UI on init before you read the user or wallets.

```tsx app/components/dashboard.tsx theme={"system"}
'use client';

import { useUser, useGetWalletAccounts, useInitStatus } from '@dynamic-labs-sdk/react-hooks';

export function Dashboard() {
  const { data: initStatus } = useInitStatus();
  const { data: user } = useUser();
  const { data: walletAccounts = [] } = useGetWalletAccounts();

  if (initStatus !== 'finished') {
    return <p>Loading…</p>;
  }

  if (!user) {
    return <p>Not signed in</p>;
  }

  return (
    <p>
      {user.email} · {walletAccounts[0]?.address}
    </p>
  );
}
```

Hooks used outside `DynamicProvider` throw `MissingProviderError`. Hooks used outside `QueryClientProvider` throw that TanStack Query is not set. See [React Hooks](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks.md).

## 4. Hydration

SDK session and wallet state exist only in the browser. If a component renders different markup on the server and on the first client render, React reports a hydration mismatch.

Prefer gating on `useInitStatus` as in the console example. If a subtree still mismatches, render it only after mount:

```tsx theme={"system"}
'use client';

import { useEffect, useState } from 'react';

export function ClientOnly({ children }: { children: React.ReactNode }) {
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  if (!mounted) return null;
  return children;
}
```

See [Hydration failed](https://docs.dynamic.xyz/docs/overview/troubleshooting/next/hydration-failed.md) for the general error. That page's `IsBrowser` helper is from the legacy React SDK (`@dynamic-labs/sdk-react-core`). Do not import it in a JavaScript SDK app.

## 5. Bundler notes

You do not need `asyncWebAssembly` or other webpack WASM flags for `@dynamic-labs-sdk/*`. Embedded wallets load in a browser container, not as a WASM module you bundle yourself.

Turbopack (`next dev` with Turbopack enabled) works with the same `"use client"` and provider setup. No extra WASM config.

If you add WalletConnect and see `Can't resolve 'encoding'` (or `pino-pretty` / `lokijs`), follow [Module not found](https://docs.dynamic.xyz/docs/overview/troubleshooting/next/module-not-found.md).

If another package in the same app requires WASM (for example a DeFi SDK), configure that package as its docs describe. Enabling `asyncWebAssembly` for those packages can break WaaS attestation modules in the Dynamic bundle. Prefer the other package's recommended workaround rather than turning WASM on globally without checking.

## Common pitfalls

| Pitfall | What to do |
| - | - |
| `createDynamicClient` inside a component or `useEffect` | Keep a module-level singleton |
| `metadata.url` | Use `metadata.universalLink` |
| `addEvmExtension(dynamicClient)` | Call `addEvmExtension()` with no arguments |
| Hooks without both providers | `QueryClientProvider` outside `DynamicProvider` |
| Importing the client from `layout.tsx` (a Server Component) | Import it only from `"use client"` files |
| Reading `user` / wallets before init finishes | Wait for `useInitStatus().data === 'finished'` |

## Related

* [React Quickstart (JS SDK)](https://docs.dynamic.xyz/docs/javascript/reference/react-quickstart.md)
* [React Hooks](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks.md)
* [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)
* [Adding extensions](https://docs.dynamic.xyz/docs/javascript/reference/adding-extensions.md)
* [Troubleshooting](https://docs.dynamic.xyz/docs/overview/troubleshooting/general.md)


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