"use client" provider, and keep SDK hooks in client components.
Before this: a Next.js App Router app, a Dynamic environment ID from the Dynamic dashboard, and the packages from the React Quickstart (JS SDK) (
@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 aftercreateDynamicClient, 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.
app/lib/dynamicClient.ts
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID in .env.local. Add your app origin to Allowed Origins in the dashboard.
For other chains, see Adding extensions. For client options, see Creating a Dynamic Client and Initializing the Dynamic Client.
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.
- React hooks
- Client functions only
app/components/providers.tsx
app/layout.tsx
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.
app/components/dashboard.tsx
DynamicProvider throw MissingProviderError. Hooks used outside QueryClientProvider throw that TanStack Query is not set. See React Hooks.
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 onuseInitStatus as in the dashboard example. If a subtree still mismatches, render it only after mount:
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 needasyncWebAssembly 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.
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.