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

# Testing Your Next.js App with the Dynamic JavaScript SDK

> Learn how to set up Jest testing for Next.js applications using the Dynamic JavaScript SDK, including mocking the client module and React hooks.

This guide walks you through setting up Jest testing for a Next.js application that uses the Dynamic JavaScript SDK. You'll learn how to properly mock the Dynamic client, providers, and hooks to write effective unit tests.

> [!NOTE]
>
> This recipe uses the Dynamic JavaScript SDK (`@dynamic-labs-sdk/client` + `@dynamic-labs-sdk/evm` + `@dynamic-labs-sdk/react-hooks`). See the [React Quickstart](https://docs.dynamic.xyz/docs/javascript/reference/react-quickstart.md) for the base setup, [React Hooks](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks.md) for provider setup, and the [React Hooks Catalog](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks-catalog.md) for every hook the SDK exports.

> [!WARNING]
>
> The JavaScript SDK is headless: it renders no DOM of its own. There are no Dynamic components or Dynamic-owned test IDs to query, so your tests must drive your own UI. See the [JavaScript SDK Overview](https://docs.dynamic.xyz/docs/javascript/overview).

> [!NOTE]
>
> This recipe is specific to **Jest + React Testing Library** for testing React components. The mocking patterns below don't have a vanilla JavaScript equivalent; they exist because tests render React output and drive a `DynamicProvider`. If your app also calls JS SDK functions directly outside of React (in a plain module), test those the same way any JavaScript module is tested: mock `@dynamic-labs-sdk/client` with `jest.mock` and assert on the calls, without React Testing Library.

## Prerequisites

* Node.js installed on your machine
* Basic familiarity with Next.js and React Testing Library
* A Dynamic Labs account with an environment ID

## Step 1: Create a New Next.js Project

First, create a new Next.js application using the latest version:

```bash theme={"system"}
npx create-next-app@latest my-test-app
```

Follow the prompts to set up your project with TypeScript and other preferred options.

## Step 2: Install the Dynamic JavaScript SDK

Install the required packages. `@tanstack/react-query` is a required peer dependency of `@dynamic-labs-sdk/react-hooks`; every hook is built on TanStack Query.

```bash theme={"system"}
npm install @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/react-hooks @tanstack/react-query
```

## Step 3: Add Dynamic to Your Application

### Create the Dynamic client module

The client is a module-level singleton. Create it once so extensions register before any component renders.

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

export const dynamicClient = createDynamicClient({
  autoInitialize: false,
  environmentId: "<YOUR_ENVIRONMENT_ID>",
  metadata: {
    name: "My Test App",
    universalLink: typeof window !== "undefined" ? window.location.origin : "",
  },
});

addEvmExtension();
void initializeClient();
```

### Configure the Layout

In Next.js App Router, the providers need to live in a client component. Wrap your app in `QueryClientProvider` (outer) and `DynamicProvider` (inner). `DynamicProvider` does not include `QueryClientProvider`, and any hook used without both will throw.

```tsx src/app/dynamic-provider-wrapper.tsx theme={"system"}
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { DynamicProvider } from "@dynamic-labs-sdk/react-hooks";
import { dynamicClient } from "@/lib/dynamicClient";

const queryClient = new QueryClient();

export default function ProviderWrapper({ children }: React.PropsWithChildren) {
  return (
    <QueryClientProvider client={queryClient}>
      <DynamicProvider client={dynamicClient}>{children}</DynamicProvider>
    </QueryClientProvider>
  );
}
```

Mount the wrapper in `src/app/layout.tsx`:

```tsx src/app/layout.tsx theme={"system"}
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import ProviderWrapper from "./dynamic-provider-wrapper";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ProviderWrapper>{children}</ProviderWrapper>
      </body>
    </html>
  );
}
```

### Add a simple page

The JS SDK is headless. For this guide we'll skip the auth form and just render a hello-world page (see the [React Quickstart](https://docs.dynamic.xyz/docs/javascript/reference/react-quickstart.md) for a full email-OTP flow, and [Authentication screens](https://docs.dynamic.xyz/docs/javascript/authentication-methods/headless-authentication.md) for the screens your app owns):

```tsx src/app/page.tsx theme={"system"}
export default function Home() {
  return (
    <div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
      <h1>Hello World</h1>
    </div>
  );
}
```

## Step 4: Configure Jest for Testing

### Install Testing Dependencies

Install Jest and React Testing Library along with necessary type definitions:

```bash theme={"system"}
npm install -D jest jest-environment-jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom ts-node @types/jest
```

### Initialize Jest Configuration

Create a basic Jest configuration file:

```bash theme={"system"}
npm init jest@latest
```

### Configure Jest for Next.js

Create a `jest.config.ts` file in your project root with the following configuration:

```ts theme={"system"}
import type { Config } from "jest";
import nextJest from "next/jest.js";

const createJestConfig = nextJest({
  dir: "./",
});

const config: Config = {
  coverageProvider: "v8",
  testEnvironment: "jsdom",
};

export default createJestConfig(config);
```

### Set Up Jest Custom Matchers

Create a `jest.setup.ts` file in your project root to configure testing utilities:

```ts theme={"system"}
import "@testing-library/jest-dom";
```

### Update Jest Configuration

Update your `jest.config.ts` to include the setup file:

```ts theme={"system"}
import type { Config } from "jest";
import nextJest from "next/jest.js";

const createJestConfig = nextJest({
  dir: "./",
});

const config: Config = {
  coverageProvider: "v8",
  testEnvironment: "jsdom",
  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
};

export default createJestConfig(config);
```

## Step 5: Write Tests for Dynamic Components

### Test the Layout Component

Create a `__tests__/layout.test.tsx` file to test your layout. Mock `DynamicProvider` so the layout renders without booting the real client, and mock the `dynamicClient` module so its `createDynamicClient` / `initializeClient` calls don't run in the test environment. `QueryClientProvider` needs no mock; a real `QueryClient` is fine in jsdom.

```tsx __tests__/layout.test.tsx theme={"system"}
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import Layout from "../src/app/layout";

// Mock the React hooks package — only DynamicProvider is needed here.
// The SDK renders nothing, so the `data-testid` below belongs to this stub, not to Dynamic.
jest.mock("@dynamic-labs-sdk/react-hooks", () => ({
  DynamicProvider: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="dynamic-provider">{children}</div>
  ),
}));

// Mock the dynamicClient module so createDynamicClient/initializeClient never run
jest.mock("@/lib/dynamicClient", () => ({
  dynamicClient: {},
}));

// Mock Next.js font imports to avoid font loading issues in tests
jest.mock("next/font/google", () => ({
  Geist: () => ({ variable: "--font-geist-sans" }),
  Geist_Mono: () => ({ variable: "--font-geist-mono" }),
}));

describe("Layout", () => {
  it("renders Layout with children", () => {
    render(<Layout>Hello World</Layout>);

    const heading = screen.getByText("Hello World");
    const provider = screen.getByTestId("dynamic-provider");

    expect(heading).toBeInTheDocument();
    expect(provider).toBeInTheDocument();
  });
});
```

### Test a Page Component

Create a `__tests__/page.test.tsx` to test a page that doesn't yet reference any Dynamic hooks:

```tsx __tests__/page.test.tsx theme={"system"}
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import Page from "../src/app/page";

describe("Page", () => {
  it("renders a heading", () => {
    render(<Page />);

    const heading = screen.getByText("Hello World");

    expect(heading).toBeInTheDocument();
  });
});
```

## Step 6: Test Components Using Dynamic Hooks

### Update Your Page to Use Hooks

Modify `src/app/page.tsx` to use the JS SDK's React hooks for displaying user information. Every state hook returns the TanStack Query result, so read the value off `data`:

```tsx src/app/page.tsx theme={"system"}
"use client";

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

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

  const isLoggedIn = Boolean(user);

  return (
    <div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
      <h1>Hello World</h1>
      <p>SDK has loaded: {initStatus === "finished" ? "Yes" : "No"}</p>
      <p>Is logged in: {isLoggedIn ? "Yes" : "No"}</p>
      <p>
        User wallets:{" "}
        {walletAccounts.length > 0 ? (
          walletAccounts.map((wallet) => (
            <span key={wallet.address}>{wallet.address}</span>
          ))
        ) : (
          <span>No wallets</span>
        )}
      </p>
    </div>
  );
}
```

### Test with Mocked Hooks

Update `__tests__/page.test.tsx` to mock the hooks and exercise the rendered states. Each mock must return the same shape as the real hook: a TanStack Query result with the value on `data`, not the value itself:

```tsx __tests__/page.test.tsx theme={"system"}
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import Page from "../src/app/page";
import {
  useGetWalletAccounts,
  useInitStatus,
  useUser,
} from "@dynamic-labs-sdk/react-hooks";

// Mock the hooks package
jest.mock("@dynamic-labs-sdk/react-hooks", () => ({
  useGetWalletAccounts: jest.fn(),
  useInitStatus: jest.fn(),
  useUser: jest.fn(),
}));

// State, query, and mutation-free hooks all return a TanStack Query result.
function mockQueryResult<T>(data: T) {
  return { data, isLoading: false, error: null, refetch: jest.fn() };
}

describe("Page", () => {
  beforeEach(() => {
    jest.mocked(useInitStatus).mockReturnValue(mockQueryResult("finished"));
    jest.mocked(useUser).mockReturnValue(mockQueryResult(null));
    jest.mocked(useGetWalletAccounts).mockReturnValue(mockQueryResult([]));
  });

  it("renders No wallets when none are connected", () => {
    render(<Page />);

    expect(screen.getByText("No wallets")).toBeInTheDocument();
    expect(screen.getByText("Is logged in: No")).toBeInTheDocument();
  });

  describe("when the user is logged in with wallets", () => {
    beforeEach(() => {
      jest.mocked(useUser).mockReturnValue(
        mockQueryResult({ id: "user_123", email: "test@example.com" })
      );
      jest.mocked(useGetWalletAccounts).mockReturnValue(
        mockQueryResult([{ address: "0x123" }])
      );
    });

    it("renders the list of wallets", () => {
      render(<Page />);

      expect(screen.getByText("0x123")).toBeInTheDocument();
      expect(screen.getByText("Is logged in: Yes")).toBeInTheDocument();
    });
  });
});
```

## Understanding the Mocking Strategy

When testing components that use the Dynamic JavaScript SDK, mock at two layers.

### 1. Mock the client module

`createDynamicClient` and `initializeClient` run when `src/lib/dynamicClient.ts` is imported. In tests, replace the whole module with a stub so neither runs:

```tsx theme={"system"}
jest.mock("@/lib/dynamicClient", () => ({
  dynamicClient: {},
}));
```

### 2. Mock the React hooks

Replace `@dynamic-labs-sdk/react-hooks` with `jest.fn()` for each hook your component uses, then drive their return values per test:

```tsx theme={"system"}
import {
  useGetWalletAccounts,
  useInitStatus,
  useUser,
} from "@dynamic-labs-sdk/react-hooks";

jest.mock("@dynamic-labs-sdk/react-hooks", () => ({
  DynamicProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  useGetWalletAccounts: jest.fn(),
  useInitStatus: jest.fn(),
  useUser: jest.fn(),
}));

function mockQueryResult<T>(data: T) {
  return { data, isLoading: false, error: null, refetch: jest.fn() };
}

beforeEach(() => {
  jest.mocked(useInitStatus).mockReturnValue(mockQueryResult("finished"));
  jest.mocked(useUser).mockReturnValue(mockQueryResult(null));
  jest.mocked(useGetWalletAccounts).mockReturnValue(mockQueryResult([]));
});
```

Match the shape of the hook you're replacing, or the component will read `undefined`:

* **State and query hooks** (`useUser`, `useGetWalletAccounts`, `useInitStatus`, `useGetNativeBalance`, …) return `{ data, isLoading, error, refetch }`. Return the value on `data`: `useUser()` gives you `{ data: user }`, not the user object.
* **Mutation hooks** (`useSendEmailOTP`, `useVerifyOTP`, `useSignMessage`, `useLogout`, …) return `{ mutate, isPending, data, error, reset }`. Mock `mutate` with a `jest.fn()` and assert it was called with the right arguments.

Only mock hooks that exist. The [React Hooks Catalog](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks-catalog.md) is the complete list of exports, and [How React Hooks Work](https://docs.dynamic.xyz/docs/javascript/reference/react-hooks-guide.md) explains the three shapes.

State hooks subscribe internally to the client's events and expose the value as a TanStack Query result. By mocking them directly, your tests stay isolated from event wiring and from network or storage access.

### Rendering with the real providers

If a test needs the real hooks rather than mocks, wrap the tree exactly as your app does: `QueryClientProvider` outside `DynamicProvider`. Create the `QueryClient` per test with retries disabled so a failing query fails fast instead of hanging the run.

Put this helper outside `__tests__/` so Jest doesn't treat it as a test suite:

```tsx test-utils.tsx theme={"system"}
import type { ReactElement } from "react";
import { render } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { DynamicProvider } from "@dynamic-labs-sdk/react-hooks";
import { dynamicClient } from "@/lib/dynamicClient";

export function renderWithProviders(ui: ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return render(
    <QueryClientProvider client={queryClient}>
      <DynamicProvider client={dynamicClient}>{ui}</DynamicProvider>
    </QueryClientProvider>
  );
}
```

> [!WARNING]
>
> This mounts the real client, so these tests initialize the SDK and make network requests. Keep them to a small number of integration-style tests and mock the hooks everywhere else.

Without both providers, hooks throw: `MissingProviderError` when `DynamicProvider` is missing, and `No QueryClient set, use QueryClientProvider to set one` when `QueryClientProvider` is missing.

## Running Your Tests

Add a test script to your `package.json`:

```json theme={"system"}
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  }
}
```

Run your tests:

```bash theme={"system"}
npm test
```

## Summary

You've set up Jest testing for a Next.js app that uses the Dynamic JavaScript SDK. The key takeaways:

* Mock `@/lib/dynamicClient` so `createDynamicClient` and `initializeClient` don't run in tests.
* Mock `@dynamic-labs-sdk/react-hooks` and drive hook return values per test (`useInitStatus`, `useUser`, `useGetWalletAccounts`, etc.), returning the value on `data`.
* For layout tests, stub `DynamicProvider` with a pass-through component.
* The SDK renders no DOM, so assert against your own components, never against Dynamic markup or test IDs.

This approach lets you test rendering and behavior without booting the real SDK, connecting wallets, or authenticating users.


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