> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aveid.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js App Router + Ave Session

> Use AveSessionProvider, useAveSession, and AveConvexBridge with the App Router and client components.

The **`@ave-id/sdk/next`** module provides **client components** for the [App Router](https://nextjs.org/docs/app): **`AveSessionProvider`**, **`useAveSession`**, and optional **`AveConvexBridge`**.

## Install

```bash theme={null}
bun add @ave-id/sdk react convex
```

`react` is a **peer dependency** (18+).

## App layout: session + Convex

`AveSessionProvider` must run in a **client** bundle. Keep a small **`Providers`** component and import it from **`layout.tsx`**.

```tsx theme={null}
// app/providers.tsx
"use client";

import { useMemo } from "react";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import {
  AveSessionProvider,
  AveConvexBridge,
} from "@ave-id/sdk/next";
import { createLocalStorageAdapter } from "@ave-id/sdk";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export function Providers({ children }: { children: React.ReactNode }) {
  const sessionOptions = useMemo(
    () => ({
      oauth: {
        clientId: process.env.NEXT_PUBLIC_AVE_CLIENT_ID!,
        redirectUri: `${process.env.NEXT_PUBLIC_APP_URL ?? ""}/callback`,
      },
      storage: createLocalStorageAdapter(),
      devtools: process.env.NODE_ENV === "development",
    }),
    []
  );

  return (
    <AveSessionProvider options={sessionOptions}>
      <ConvexProvider client={convex}>
        <AveConvexBridge client={convex}>{children}</AveConvexBridge>
      </ConvexProvider>
    </AveSessionProvider>
  );
}
```

```tsx theme={null}
// 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>
  );
}
```

## OAuth callback route

Use **`completeOAuthCallback`** from **`@ave-id/sdk/client`** in a **client** page (or call **`finishPkceLogin`** + **`session.setTokensFromResponse`**). The provider’s **`AveSession`** instance must be the same one — obtain it via **`useAveSession().session`** in a child component, or structure your callback as a component under **`AveSessionProvider`** and pass **`session`** from context.

Example: a **`/callback`** page that mounts only after login redirect can use **`useAveSession`** to run **`completeOAuthCallback`** once when **`searchParams.code`** is present.

## Hooks

* **`useAveSession()`** — **`{ session, status, snapshot, isHydrated }`**. Wait for **`isHydrated`** before treating **`status === "signedOut"`** as definitive on first paint.
* **`getAppKeyBase64()`** on **`session`** for E2EE after hydrate.

## Related

* [Ave Session](/guides/ave-session-and-tokens)
* [Convex custom auth](/guides/convex-custom-auth)
