> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/get-convex/convex-backend/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js integration

> Using Convex with Next.js App Router, Server Components, and Server Actions

The Next.js module provides helpers for integrating Convex with Next.js features including Server Components, Server Actions, and Route Handlers.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install convex next react
  ```

  ```bash yarn theme={null}
  yarn add convex next react
  ```

  ```bash pnpm theme={null}
  pnpm add convex next react
  ```
</CodeGroup>

## Setup

All exported functions use the `NEXT_PUBLIC_CONVEX_URL` environment variable by default. The `npx convex dev` command automatically sets this during local development.

```bash .env.local theme={null}
NEXT_PUBLIC_CONVEX_URL=https://small-mouse-123.convex.cloud
```

## Server Components

### fetchQuery

Execute a query from a Server Component:

```typescript theme={null}
async function fetchQuery<Query extends FunctionReference<"query">>(
  query: Query,
  args?: Query["_args"],
  options?: NextjsOptions
): Promise<FunctionReturnType<Query>>
```

**Usage:**

```tsx theme={null}
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export default async function TasksPage() {
  const tasks = await fetchQuery(api.tasks.list, { completed: false });

  return (
    <div>
      {tasks.map((task) => (
        <div key={task._id}>{task.text}</div>
      ))}
    </div>
  );
}
```

### fetchMutation

Execute a mutation from a Server Component or Server Action:

```typescript theme={null}
async function fetchMutation<Mutation extends FunctionReference<"mutation">>(
  mutation: Mutation,
  args?: Mutation["_args"],
  options?: NextjsOptions
): Promise<FunctionReturnType<Mutation>>
```

**Usage in a Server Action:**

```tsx theme={null}
import { fetchMutation } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export async function createTask(formData: FormData) {
  "use server";
  
  const text = formData.get("text") as string;
  await fetchMutation(api.tasks.create, { text });
}
```

### fetchAction

Execute an action from a Server Component or Server Action:

```typescript theme={null}
async function fetchAction<Action extends FunctionReference<"action">>(
  action: Action,
  args?: Action["_args"],
  options?: NextjsOptions
): Promise<FunctionReturnType<Action>>
```

**Usage:**

```tsx theme={null}
import { fetchAction } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export default async function SummaryPage() {
  const summary = await fetchAction(api.ai.generateSummary, {
    text: "Some long text..."
  });

  return <div>{summary}</div>;
}
```

## Preloading for Client Components

### preloadQuery

Preload query data in a Server Component to pass to a Client Component:

```typescript theme={null}
async function preloadQuery<Query extends FunctionReference<"query">>(
  query: Query,
  args?: Query["_args"],
  options?: NextjsOptions
): Promise<Preloaded<Query>>
```

**Server Component:**

```tsx theme={null}
import { preloadQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import TaskList from "./TaskList";

export default async function TasksPage() {
  const preloadedTasks = await preloadQuery(api.tasks.list, {
    completed: false
  });

  return <TaskList preloadedTasks={preloadedTasks} />;
}
```

**Client Component:**

```tsx theme={null}
"use client";

import { Preloaded, usePreloadedQuery } from "convex/react";
import { api } from "@/convex/_generated/api";

export default function TaskList({
  preloadedTasks
}: {
  preloadedTasks: Preloaded<typeof api.tasks.list>;
}) {
  const tasks = usePreloadedQuery(preloadedTasks);

  return (
    <div>
      {tasks.map((task) => (
        <div key={task._id}>{task.text}</div>
      ))}
    </div>
  );
}
```

### usePreloadedQuery

Use preloaded data in a Client Component and subscribe to updates:

```tsx theme={null}
import { usePreloadedQuery, Preloaded } from "convex/react";
import { api } from "@/convex/_generated/api";

function TaskList({ preloaded }: { 
  preloaded: Preloaded<typeof api.tasks.list> 
}) {
  const tasks = usePreloadedQuery(preloaded);
  // tasks will update reactively after initial render
  
  return (
    <div>
      {tasks.map((task) => <div key={task._id}>{task.text}</div>)}
    </div>
  );
}
```

## Options

All functions accept a `NextjsOptions` object:

```typescript theme={null}
type NextjsOptions = {
  token?: string;
  url?: string;
  skipConvexDeploymentUrlCheck?: boolean;
};
```

**Parameters:**

* `token` - JWT authentication token
* `url` - Convex deployment URL (defaults to `process.env.NEXT_PUBLIC_CONVEX_URL`)
* `skipConvexDeploymentUrlCheck` - Skip URL validation for self-hosted backends

**Example with authentication:**

```tsx theme={null}
import { auth } from "@/auth";
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export default async function ProfilePage() {
  const session = await auth();
  
  const profile = await fetchQuery(
    api.users.profile,
    {},
    { token: session?.token }
  );

  return <div>{profile.name}</div>;
}
```

## Route Handlers

Use Convex functions in Next.js API routes:

```typescript theme={null}
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import { NextResponse } from "next/server";

export async function GET() {
  const tasks = await fetchQuery(api.tasks.list, {});
  return NextResponse.json(tasks);
}

export async function POST(request: Request) {
  const body = await request.json();
  const taskId = await fetchMutation(api.tasks.create, body);
  return NextResponse.json({ id: taskId });
}
```

## Client-side usage

For client-side reactivity, use the standard React hooks with `ConvexProvider`:

**Layout or root component:**

```tsx theme={null}
"use client";

import { ConvexProvider, ConvexReactClient } from "convex/react";

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

export function ConvexClientProvider({
  children
}: {
  children: React.ReactNode;
}) {
  return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}
```

**Client Component:**

```tsx theme={null}
"use client";

import { useQuery, useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";

export default function Tasks() {
  const tasks = useQuery(api.tasks.list, {});
  const createTask = useMutation(api.tasks.create);

  return (
    <div>
      {tasks?.map((task) => <div key={task._id}>{task.text}</div>)}
      <button onClick={() => createTask({ text: "New task" })}>
        Add Task
      </button>
    </div>
  );
}
```

## Patterns

### Server Component with Client Component

Combine server-side data fetching with client-side reactivity:

```tsx theme={null}
// app/page.tsx (Server Component)
import { preloadQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import TaskList from "./TaskList";

export default async function Home() {
  const preloadedTasks = await preloadQuery(api.tasks.list, {});
  return <TaskList preloaded={preloadedTasks} />;
}

// TaskList.tsx (Client Component)
"use client";
import { usePreloadedQuery, useMutation, Preloaded } from "convex/react";
import { api } from "@/convex/_generated/api";

export default function TaskList({
  preloaded
}: {
  preloaded: Preloaded<typeof api.tasks.list>;
}) {
  const tasks = usePreloadedQuery(preloaded);
  const createTask = useMutation(api.tasks.create);

  return (
    <div>
      {tasks.map((task) => <div key={task._id}>{task.text}</div>)}
      <button onClick={() => createTask({ text: "New" })}>
        Add
      </button>
    </div>
  );
}
```

### Server Actions

```tsx theme={null}
"use client";

import { createTask } from "./actions";

export default function CreateTaskForm() {
  return (
    <form action={createTask}>
      <input name="text" />
      <button type="submit">Create</button>
    </form>
  );
}

// actions.ts
"use server";
import { fetchMutation } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export async function createTask(formData: FormData) {
  const text = formData.get("text") as string;
  await fetchMutation(api.tasks.create, { text });
}
```

### Authentication

Pass auth tokens from your auth provider:

```tsx theme={null}
import { auth } from "@clerk/nextjs";
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export default async function ProtectedPage() {
  const { getToken } = auth();
  const token = await getToken({ template: "convex" });
  
  const data = await fetchQuery(
    api.users.getCurrentUser,
    {},
    { token }
  );

  return <div>{data.name}</div>;
}
```

## Best practices

* Use `preloadQuery` to hydrate Client Components with initial data
* Execute mutations in Server Actions for progressive enhancement
* Use `fetchQuery` for server-only pages that don't need reactivity
* Combine server and client rendering for optimal performance
* Pass authentication tokens via the `token` option
* Use environment variables for the Convex URL
* Leverage Next.js caching strategies with `fetchQuery`

## Type safety

All functions are fully type-safe with generated API types:

```tsx theme={null}
import { api } from "@/convex/_generated/api";

// Fully type-checked
const tasks = await fetchQuery(api.tasks.list, {
  completed: false // Type-checked
});
// tasks has type Task[]

const preloaded = await preloadQuery(api.tasks.list, {});
// preloaded has type Preloaded<typeof api.tasks.list>
```

## Caching

Next.js caches `fetchQuery` results by default. Control caching with Next.js options:

```tsx theme={null}
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

// Force dynamic (no caching)
export const dynamic = 'force-dynamic';

// Or use Next.js revalidation
export const revalidate = 60; // Revalidate every 60 seconds

export default async function Page() {
  const tasks = await fetchQuery(api.tasks.list, {});
  return <div>{tasks.length} tasks</div>;
}
```
