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
Fork and clone the repository
Set up environment variables
cp -n ./docs/.env.example ./docs/.env
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
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
| Area | Notes |
|---|
| Bug fixes | Check issues labeled good first issue |
| Framework integrations | Prefer framework-agnostic solutions; keep minimal |
| Core plugins | Open an issue to discuss before implementing |
| Community plugins | Develop independently and share on Discord |
| Documentation | Fix typos, add examples, keep docs in sync with code |
Need help?