Convex + Better Auth
Framework Guides

React (Vite SPA)

Install and configure Convex + Better Auth for React.

Check out a complete Convex + Better Auth example with React in the GitHub repo.

Installation

Install packages

Install the component, a pinned version of Better Auth, and ensure the latest version of Convex.

This component requires Convex 1.25.0 or later.
npm install better-auth@1.3.8 --save-exact
npm install convex@latest @convex-dev/better-auth

Register the component

Register the Better Auth component in your Convex project.

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;

Add Convex auth config

Add a convex/auth.config.ts file to configure Better Auth as an authentication provider.

convex/auth.config.ts
export default {
  providers: [
    {
      domain: process.env.CONVEX_SITE_URL,
      applicationID: "convex",
    },
  ],
};

Set environment variables

Generate a secret for encryption and generating hashes. Use the command below if you have openssl installed, or use the button to generate a random value instead. Or generate your own however you like.

npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)

Add your site URL to your Convex deployment.

npx convex env set SITE_URL http://localhost:5173

Add environment variables to the .env.local file created by npx convex dev. It will be picked up by your framework dev server.

.env.local
# Deployment used by \`npx convex dev\`
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name

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

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

# Your local site URL
SITE_URL=http://localhost:5173

Create a Better Auth instance

Create a Better Auth instance and initialize the component.

Some Typescript errors will show until you save the file.
convex/auth.ts
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex, crossDomain } 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";

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>,
  { optionsOnly } = { optionsOnly: false },
) => {
  return betterAuth({
    // disable logging when createAuth is called just to generate options.
    // this is not required, but there's a lot of noise in logs without it.
    logger: {
      disabled: optionsOnly,
    },
    trustedOrigins: [siteUrl],
    database: authComponent.adapter(ctx),
    // Configure simple, non-verified email/password to get started
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: false,
    },
    plugins: [
      // The cross domain plugin is required for client side frameworks
      crossDomain({ siteUrl }),
      // The Convex plugin is required for Convex compatibility
      convex(),
    ],
  });
};

// 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);
  },
});

Create a Better Auth client instance

Create a Better Auth client instance for interacting with the Better Auth server from your client.

src/lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
import {
  convexClient,
  crossDomainClient,
} from "@convex-dev/better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: import.meta.env.VITE_CONVEX_SITE_URL,
  plugins: [convexClient(), crossDomainClient()],
});

Mount handlers

Register Better Auth route handlers on your Convex deployment.

convex/http.ts
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";

const http = httpRouter();

// CORS handling is required for client side frameworks
authComponent.registerRoutes(http, createAuth, { cors: true });

export default http;

Set up Convex client provider

Wrap your app with the ConvexBetterAuthProvider component.

src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { ConvexReactClient } from "convex/react";
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react"; 
import { authClient } from "@/lib/auth-client"; 

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string, {
  // Optionally pause queries until the user is authenticated
  expectAuth: true, 
});

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <ConvexBetterAuthProvider client={convex} authClient={authClient}>
      <App />
    </ConvexBetterAuthProvider>
  </React.StrictMode>
);

You're done!

You're now ready to start using Better Auth with Convex.