Skip to main content
Thank you for your interest in contributing to Better Auth! This page covers everything you need to get started.

GitHub Issues

Browse open issues or report a bug.

Discord

Ask questions and discuss ideas with the community.

Development setup

Make sure you have Node.js LTS and pnpm installed before continuing.
1

Fork and clone the repository

Visit github.com/better-auth/better-auth and click Fork. Then clone your fork:
git clone https://github.com/YOUR-USERNAME/better-auth.git
cd better-auth
2

Install dependencies

pnpm install
3

Set up environment variables

cp -n ./docs/.env.example ./docs/.env
4

Create a feature branch

git remote add upstream https://github.com/better-auth/better-auth.git
git checkout canary
git pull upstream canary
git checkout -b feat/your-feature-name
5

Start the development server

# Main package
pnpm dev

# Documentation site
pnpm -F docs dev

Running tests

Better Auth uses Vitest. Test files live next to the source files they cover.
# Run a specific test file
vitest packages/better-auth/src/auth/auth.test.ts

# Run tests matching a pattern
vitest packages/better-auth/src -t "sign up"
Do not run pnpm test at the repo root — it runs all tests across all packages and takes a long time. Target specific files or patterns instead.
Adapter tests (PostgreSQL, MySQL, etc.) require Docker:
docker compose up -d
vitest e2e/adapter

Using the test instance helper

import { describe, it, expect } from "vitest";
import { getTestInstance } from "better-auth/test";

describe("my feature", () => {
  it("works as expected", async () => {
    const { client } = await getTestInstance();
    const result = await client.signUp.email({
      email: "test@example.com",
      password: "password",
      name: "Test User",
    });
    expect(result.data).toBeDefined();
  });
});
The helper also exposes runWithUser and signInWithTestUser for authenticated test scenarios:
const { client, runWithUser } = await getTestInstance();

await runWithUser("user@example.com", "password", async (headers) => {
  const session = await client.getSession({ fetchOptions: { headers } });
  expect(session.data).not.toBeNull();
});

Code style

Better Auth uses Biome for formatting and linting.
# Check formatting
pnpm format:check

# Fix formatting
pnpm format

# Lint
pnpm lint

# Fix lint issues
pnpm lint:fix

# Type check
pnpm typecheck
All CI checks must pass before a PR is merged. Run the checks locally before opening a PR:
pnpm format:check && pnpm lint && pnpm typecheck
Style rules:
  • Use tabs for indentation in TypeScript; 2 spaces in JSON.
  • Avoid any types and unsafe typecasts.
  • Prefer functions and plain objects over classes.
  • Do not use runtime-specific APIs like Buffer in library code. Use Uint8Array instead.

PR guidelines

  • Target the canary branch, not main.
  • Use Conventional Commits for commit messages:
    feat(scope): add X
    fix(scope): resolve Y
    docs: update contributing guide
    chore: bump dependencies
    
  • Keep PRs focused on a single change.
  • Add tests for new features and bug fixes.
  • Update documentation when changing public APIs.
  • If a PR fixes a numbered GitHub issue, add a JSDoc @see comment to the relevant test:
    /**
     * @see https://github.com/better-auth/better-auth/issues/1234
     */
    it("should handle the previously broken behavior", async () => { /* ... */ });
    

Contribution areas

AreaNotes
Bug fixesCheck issues labeled good first issue
Framework integrationsPrefer framework-agnostic solutions; keep minimal
Core pluginsOpen an issue to discuss before implementing
Community pluginsDevelop independently and share on Discord
DocumentationFix typos, add examples, keep docs in sync with code

Need help?