Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/get-convex/better-auth/llms.txt

Use this file to discover all available pages before exploring further.

A complete working example is available in the GitHub repo.
Next.js uses the convexBetterAuthNextJs server utilities for authenticated SSR, server components, and server actions. Auth requests are proxied from your Next.js route handler to your Convex deployment.

Installation

1

Install packages

Install the component, ensure you have the latest version of Convex, and install a pinned version of Better Auth.
This component requires Convex 1.25.0 or later. Install better-auth@1.5.3 with an exact pin to avoid unexpected breaking changes.
npm install convex@latest @convex-dev/better-auth
npm install better-auth@1.5.3 --save-exact
2

Register the component

Register the Better Auth component in your Convex project configuration.
convex/convex.config.ts
import { defineApp } from "convex/server";
import betterAuth from "@convex-dev/better-auth/convex.config";

const app = defineApp();
app.use(betterAuth);

export default app;
3

Add Convex auth config

Add a convex/auth.config.ts file to configure Better Auth as a Convex authentication provider.
convex/auth.config.ts
import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
import type { AuthConfig } from "convex/server";

export default {
  providers: [getAuthConfigProvider()],
} satisfies AuthConfig;
4

Set environment variables

Generate a secret for encryption and hashing, and set your site URL on your Convex deployment.
npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
npx convex env set SITE_URL http://localhost:3000
Then add the client-side environment variables to the .env.local file created by npx convex dev.
.env.local
# Deployment used by `npx convex dev`
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name

NEXT_PUBLIC_CONVEX_URL=https://adjective-animal-123.convex.cloud

# Same as NEXT_PUBLIC_CONVEX_URL but ends in .site
NEXT_PUBLIC_CONVEX_SITE_URL=https://adjective-animal-123.convex.site

# Your local site URL
NEXT_PUBLIC_SITE_URL=http://localhost:3000
5

Create a Better Auth server instance

Create the Better Auth instance in convex/auth.ts and initialize the component.
Some TypeScript errors will appear until you save the file and generated types are updated.
convex/auth.ts
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { query } from "./_generated/server";
import { betterAuth } from "better-auth/minimal";
import authConfig from "./auth.config";

const siteUrl = process.env.SITE_URL!;

// The component client has methods needed for integrating Convex with Better Auth,
// as well as helper methods for general use.
export const authComponent = createClient<DataModel>(components.betterAuth);

export const createAuth = (ctx: GenericCtx<DataModel>) => {
  return betterAuth({
    baseURL: siteUrl,
    database: authComponent.adapter(ctx),
    // Configure simple, non-verified email/password to get started
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: false,
    },
    plugins: [
      // The Convex plugin is required for Convex compatibility
      convex({ authConfig }),
    ],
  });
};

// Example function for getting the current user
// Feel free to edit, omit, etc.
export const getCurrentUser = query({
  args: {},
  handler: async (ctx) => {
    return authComponent.getAuthUser(ctx);
  },
});
6

Create a Better Auth client instance

Create a Better Auth client instance for your frontend.
src/lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
import { convexClient } from "@convex-dev/better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [convexClient()],
});
7

Configure Next.js server utilities

Create server utilities for authenticated SSR, server components, and server actions using convexBetterAuthNextJs. This gives you preloadAuthQuery, fetchAuthMutation, isAuthenticated, getToken, and more.
src/lib/auth-server.ts
import { convexBetterAuthNextJs } from "@convex-dev/better-auth/nextjs";

export const {
  handler,
  preloadAuthQuery,
  isAuthenticated,
  getToken,
  fetchAuthQuery,
  fetchAuthMutation,
  fetchAuthAction,
} = convexBetterAuthNextJs({
  convexUrl: process.env.NEXT_PUBLIC_CONVEX_URL!,
  convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!,
});
8

Mount HTTP route handlers

Register the Better Auth route handlers on your Convex HTTP router.
convex/http.ts
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";

const http = httpRouter();

authComponent.registerRoutes(http, createAuth);

export default http;
Then add a Next.js catch-all route handler to proxy auth requests from Next.js to your Convex deployment.
app/api/auth/[...all]/route.ts
import { handler } from "@/lib/auth-server";

export const { GET, POST } = handler;
9

Set up the Convex client provider

Create a ConvexClientProvider component using ConvexBetterAuthProvider. This replaces the standard ConvexProvider and accepts an initialToken for SSR hydration.
app/ConvexClientProvider.tsx
"use client";

import { ReactNode } from "react";
import { ConvexReactClient } from "convex/react";
import { authClient } from "@/lib/auth-client";
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";

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

export function ConvexClientProvider({
  children,
  initialToken,
}: {
  children: ReactNode;
  initialToken?: string | null;
}) {
  return (
    <ConvexBetterAuthProvider
      client={convex}
      authClient={authClient}
      initialToken={initialToken}
    >
      {children}
    </ConvexBetterAuthProvider>
  );
}
You’re now ready to use Better Auth with Convex in your Next.js app.

Usage

See Basic Usage for sign-in, sign-up, and session management patterns. The notes below are specific to Next.js.

SSR with server components

Convex queries can be preloaded in server components and rendered reactively in client components using preloadAuthQuery and usePreloadedAuthQuery. Preloading in a server component:
app/(auth)/(dashboard)/page.tsx
import { preloadAuthQuery } from "@/lib/auth-server";
import { api } from "@/convex/_generated/api";

const Page = async () => {
  const [preloadedUserQuery] = await Promise.all([
    preloadAuthQuery(api.auth.getCurrentUser),
    // Load multiple queries in parallel if needed
  ]);

  return (
    <div>
      <Header preloadedUserQuery={preloadedUserQuery} />
    </div>
  );
};

export default Page;
Rendering preloaded data in a client component:
app/(auth)/(dashboard)/header.tsx
import { usePreloadedAuthQuery } from "@convex-dev/better-auth/nextjs/client";
import { api } from "@/convex/_generated/api";

export const Header = ({
  preloadedUserQuery,
}: {
  preloadedUserQuery: Preloaded<typeof api.auth.getCurrentUser>;
}) => {
  const user = usePreloadedAuthQuery(preloadedUserQuery);
  return (
    <div>
      <h1>{user?.name}</h1>
    </div>
  );
};

export default Header;

Using Better Auth in server code

Better Auth’s auth.api methods normally run in your server code, but with Convex as your backend, these methods run inside Convex functions. Call those functions from server actions using fetchAuthMutation or fetchAuthQuery. Authentication is handled automatically using session cookies. Here’s an example using changePassword. The Better Auth auth.api method runs inside a Convex mutation:
convex/users.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { createAuth, authComponent } from "./auth";

export const updateUserPassword = mutation({
  args: {
    currentPassword: v.string(),
    newPassword: v.string(),
  },
  handler: async (ctx, args) => {
    const { auth, headers } = await authComponent.getAuth(createAuth, ctx);
    await auth.api.changePassword({
      body: {
        currentPassword: args.currentPassword,
        newPassword: args.newPassword,
      },
      headers,
    });
  },
});
Calling that mutation from a Next.js server action:
app/actions.ts
"use server";

import { fetchAuthMutation } from "@/lib/auth-server";
import { api } from "../convex/_generated/api";

export async function updatePassword({
  currentPassword,
  newPassword,
}: {
  currentPassword: string;
  newPassword: string;
}) {
  await fetchAuthMutation(api.users.updatePassword, {
    currentPassword,
    newPassword,
  });
}