# Server-side rendering

> Learn how to use Wallet APIs with server-side rendering.

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

> ⚠️ **Common Issue**: If SSR is not set up correctly, you may see sessions resetting or users getting logged out unexpectedly. Make sure to follow this guide fully to preserve session state.

When using Wallet APIs in a server-side rendered setting, you may see inconsistencies in account state between the server and the client. This leads to flashes of content when logged in. To avoid this, load the account state optimistically on the server and pass it to the client.

## Update your config

To enable server-side rendering support, you need to set `ssr: true` when creating a config.

<Tabs>
  <Tab title="React" language="react">

When using React, make the config a function so that you can call it once per request, which allows for request-based isolation of the account state.

```ts twoslash config.ts {9}
import { createConfig } from "@account-kit/react";
import { sepolia, alchemy } from "@account-kit/infra";

export const config = () =>
  createConfig({
    // required
    transport: alchemy({ rpcUrl: "/api/rpc" }),
    chain: sepolia,
    ssr: true,
  });
```

This setting will defer hydration of the account state to the client after the initial mount.

## Persisting the account state

### Cookie storage

To consistently pass the state between the server and the client, pass in a cookie storage to the `config` object created above. The cookie storage allows the client state to be serialized to a cookie which is passed to the server on each request. This gives the server access to certain parts of the account state when rendering, ensuring a consistent render between client and server (e.g. address displayed in the top nav). Instances that can only be created on the client are still not available on the server, however. This includes the authentication or smart wallet instances.

```ts twoslash config.ts {1,12-13}
import { createConfig, cookieStorage } from "@account-kit/react";
import { sepolia, alchemy } from "@account-kit/infra";
import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient();

export const config = () =>
  createConfig({
    // required
    transport: alchemy({ rpcUrl: "/api/rpc" }),
    chain: sepolia,
    ssr: true,
    storage: cookieStorage,
  });
```

Now, depending on your application, you can get the state from cookies and pass in the `initialState` to the `AlchemyAccountProvider` to hydrate the account state on the client.

### Next.js App Directory

If you are using Next.js App Directory, read the cookie state and pass it to the providers:

<CodeBlocks>

```tsx app/layout.tsx
// @noErrors
import React from "react";
import { cookieToInitialState } from "@account-kit/core";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { headers } from "next/headers";
import { config } from "./config";
import "./globals.css";
import { Providers } from "./providers";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Embedded Accounts Getting Started",
  description: "Embedded Accounts Quickstart Guide",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  // This will allow us to persist state across page boundaries
  const initialState = cookieToInitialState(
    // the config here is just used to compute the initial state
    config(),
    headers().get("cookie") ?? undefined,
  );

  return (
    <html lang="en">
      <body className={inter.className}>
        <Providers initialState={initialState}>{children}</Providers>
      </body>
    </html>
  );
}
```

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

import React, { useRef } from "react";
import { AlchemyClientState } from "@account-kit/core";
import {
  AlchemyAccountProvider,
  type AlchemyAccountsConfigWithUI,
} from "@account-kit/react";
import { QueryClientProvider } from "@tanstack/react-query";
import { PropsWithChildren, Suspense } from "react";
import { config, queryClient } from "./config";

export const Providers = (
  props: PropsWithChildren<{ initialState?: AlchemyClientState }>,
) => {
  const ref = useRef<AlchemyAccountsConfigWithUI>();
  if (!ref.current) {
    ref.current = config();
  }

  return (
    <Suspense>
      <QueryClientProvider client={queryClient}>
        <AlchemyAccountProvider
          config={ref.current!}
          queryClient={queryClient}
          initialState={props.initialState}
        >
          {props.children}
        </AlchemyAccountProvider>
      </QueryClientProvider>
    </Suspense>
  );
};
```

```tsx config.ts
import { createConfig, cookieStorage } from "@account-kit/react";
import { sepolia, alchemy } from "@account-kit/infra";
import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient();

// When using SSR, you need to be able to create a config per request
// This is to avoid sharing state between requests (eg. signed in users)
export const config = () =>
  createConfig({
    transport: alchemy({ rpcUrl: "/api/rpc" }),
    chain: sepolia,
    ssr: true,
    storage: cookieStorage,
  });
```

</CodeBlocks>

  </Tab>
  <Tab title="Other Javascript">

```ts
import { createConfig } from "@account-kit/core";
import { sepolia, alchemy } from "@account-kit/infra";

export const config = createConfig({
  transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
  chain: sepolia,
  ssr: true,
});
```

This setting defers hydration of the Wallet APIs state until you call the `hydrate` method on mount.

## Hydrate the account state

Now that you've set up your config for SSR, manually hydrate the state on the client. Call the `hydrate` method exported by Wallet APIs core.

<CodeBlocks>

```ts hydrate.ts
import { hydrate } from "@account-kit/core";
import { config } from "./config";

const { onMount } = hydrate(config);

// when your component has mounted on the client, call the onMount method
// how you do this will depend on your framework, but here we'll just check for `window`
// to be defined
if (typeof window !== "undefined") {
  onMount();
}
```

```ts config.ts
import { createConfig } from "@account-kit/core";
import { alchemy, sepolia } from "@account-kit/infra";

export const config = createConfig({
  transport: alchemy({ apiKey: "YOUR_API_KEY" }),
  chain: sepolia,
  ssr: true,
});
```

</CodeBlocks>

## Persisting the account state

To consistently pass the state between the server and the client, pass in a cookie storage to the `config` object created above. The cookie storage allows the client state to be serialized to a cookie which is passed to the server on each request. This gives the server access to certain parts of the account state when rendering, ensuring a consistent render between client and server (e.g. address displayed in the top nav). Instances that can only be created on the client are still not available on the server, however. This includes the authentication or smart wallet instances.

```ts twoslash {7-8}
import { createConfig, cookieStorage } from "@account-kit/core"; // Add cookieStorage import
import { sepolia, alchemy } from "@account-kit/infra";

export const config = createConfig({
  transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
  chain: sepolia,
  ssr: true,
  storage: cookieStorage,
});
```

Now you can get the initial state from cookies and pass it to the `hydrate` method to hydrate the account state on the client or on the server.

<CodeBlocks>

```ts hydrate.ts {7}
import { cookieToInitialState, hydrate } from "@account-kit/core";
import { config } from "./config";

// this is an example of how to get the cookie on the client
// but on the server you might get it from the `req.cookies` object
// it all depends on your framework
const initialState = cookieToInitialState(config, document.cookie);
const { onMount } = hydrate(config, initialState);

if (typeof window !== "undefined") {
  onMount();
}
```

```ts config.ts {7-8}
import { createConfig, cookieStorage } from "@account-kit/core";
import { sepolia, alchemy } from "@account-kit/infra";

export const config = createConfig({
  transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
  chain: sepolia,
  ssr: true,
  storage: cookieStorage,
});
```

</CodeBlocks>

  </Tab>
</Tabs>