# Next.js SSR

The SDK relies on browser APIs -- Web Workers, IndexedDB, and WebAssembly -- that are not available during server-side rendering. This guide covers the patterns you need to keep FHE operations on the client while still using Next.js App Router and SSR layouts.

## Steps

### 1. Understand the constraint

The FHE relayer runs encryption and decryption inside a Web Worker backed by a WASM binary. IndexedDB stores encrypted keypairs. None of these APIs exist in Node.js or during SSR.

This means:

* You cannot import `RelayerWeb`, `ZamaProvider`, or any SDK hook in a Server Component
* You cannot create the relayer or signer at module level in a file that runs on the server

### 2. Mark SDK components with `"use client"`

Any component that imports from `@zama-fhe/react-sdk` must be a Client Component:

```tsx
"use client";

import { useConfidentialBalance } from "@zama-fhe/react-sdk";

export function TokenBalance({ address }: { address: string }) {
  const { data: balance, isLoading } = useConfidentialBalance({
    tokenAddress: address,
  });

  if (isLoading) return <span>Loading...</span>;
  return <span>{balance?.toString()}</span>;
}
```

### 3. Place `ZamaProvider` inside a client component

Create a dedicated client component that sets up the SDK providers. This keeps the relayer and signer initialization off the server.

```tsx
// app/providers.tsx
"use client";

import { WagmiProvider, createConfig, http } from "wagmi";
import { sepolia } from "wagmi/chains";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ZamaProvider, RelayerWeb, indexedDBStorage } from "@zama-fhe/react-sdk";
import { WagmiSigner } from "@zama-fhe/react-sdk/wagmi";

const wagmiConfig = createConfig({
  chains: [sepolia],
  transports: { [sepolia.id]: http() },
});

const signer = new WagmiSigner({ config: wagmiConfig });
const relayer = new RelayerWeb({
  getChainId: () => signer.getChainId(),
  transports: {
    [sepolia.id]: {
      relayerUrl: "/api/relayer/11155111",
      network: "https://sepolia.infura.io/v3/YOUR_KEY",
    },
  },
});

const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <ZamaProvider relayer={relayer} signer={signer} storage={indexedDBStorage}>
          {children}
        </ZamaProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}
```

### 4. Use the provider in your layout

The root layout is a Server Component by default. Import the client `Providers` wrapper and nest your pages inside it:

```tsx
// app/layout.tsx
import { Providers } from "./providers";

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

The layout file itself does not need `"use client"` -- it only imports a component that is already marked as a Client Component.

### 5. Avoid creating SDK objects in server components

A common mistake is initializing the relayer or signer in a shared module that gets imported by both server and client code:

```ts
// lib/sdk.ts — DO NOT do this
import { RelayerWeb } from "@zama-fhe/sdk";

// This runs during SSR and crashes — Web Worker is not available
export const relayer = new RelayerWeb({ ... });
```

Instead, keep all SDK initialization inside a `"use client"` file (like the `Providers` component above), or gate it behind a dynamic import:

```ts
// lib/sdk.ts — safe alternative
export async function getRelayer() {
  const { RelayerWeb } = await import("@zama-fhe/sdk");
  return new RelayerWeb({ ... });
}
```

### 6. Example: page with a confidential balance

Putting it all together -- a Next.js page that displays a confidential token balance:

```tsx
// app/portfolio/page.tsx (Server Component — no SDK imports here)
import { TokenBalance } from "@/components/token-balance";

export default function PortfolioPage() {
  return (
    <div>
      <h1>My Portfolio</h1>
      <TokenBalance address="0xEncryptedERC20" />
    </div>
  );
}
```

```tsx
// components/token-balance.tsx (Client Component)
"use client";

import { useConfidentialBalance } from "@zama-fhe/react-sdk";

export function TokenBalance({ address }: { address: string }) {
  const { data: balance, isLoading } = useConfidentialBalance({
    tokenAddress: address,
  });

  if (isLoading) return <span>Decrypting...</span>;
  return <span>{balance?.toString()}</span>;
}
```

The server renders the page shell, and the `TokenBalance` client component hydrates on the browser where FHE APIs are available.

## Next steps

* [ZamaProvider](https://github.com/zama-ai/sdk/blob/main/reference/react/ZamaProvider/README.md) -- all provider props and configuration
* [useConfidentialBalance](https://github.com/zama-ai/sdk/blob/main/reference/react/useConfidentialBalance/README.md) -- balance hook API reference
* [Provider Setup](https://github.com/zama-ai/sdk/blob/main/guides/configuration/README.md) -- full examples for wagmi, viem, and ethers setups


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zama.org/protocol/sdk/guides/nextjs-ssr.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
