Reference

Contributing

Set up the repository, run the playground and tests, and add a feature the way the module does.

Thanks for helping! Bug reports, fixes, docs and features are all welcome. For something big, open an issue first, so we can agree on the approach before you write it.

Setting up

You need Node 22 or 24 and pnpm (the version in package.json's packageManager; corepack enable sets it up).

Clone and install

Terminal
git clone https://github.com/treygrr/nuxt-better-auth-ui.git
cd nuxt-better-auth-ui
pnpm install

Configure the playground

Terminal
cp playground/.env.example playground/.env

The defaults work as they are. Its comments explain the optional extras: GitHub sign-in, captcha keys, Spanish.

Prepare and start it

Terminal
pnpm dev:prepare
pnpm dev

The playground runs on http://localhost:3001. Sign in as admin@example.com with password123.

The repository

It's a pnpm workspace:

FolderWhat it is
src/The module: module.ts (its options and how it adds Better Auth's plugins), runtime/app (pages, components, words), runtime/server (its own routes and Better Auth plugins)
playground/A Nuxt app using the module with every feature on, and mock data
test/API tests, and browser tests in test/browser/
docs/This site

The playground's data

On startup, the playground seeds an admin and 40 mock users from playground/.env: join dates over six months, verified and unverified emails, more admins, bans (permanent, expiring, expired), a moderator (lucas.haddad@example.com), linked GitHub, Google and Discord accounts, and sessions on different devices. Mock users share the admin's password.

It only adds users that are missing, so delete playground/.data/auth.sqlite to start over.

Emails and texts don't leave the playground: they go to an outbox, at /api/_playground/outbox and /api/_playground/sms.

Checks

Run these before opening a pull request; CI runs them too, on Node 22 and 24.

Terminal
pnpm fmt        # Prettier (pnpm fmt:fix to fix)
pnpm typecheck  # the module, its server code and the playground
pnpm test       # builds everything, then runs the API and browser tests

pnpm test builds the module and the playground, starts the playground once on port 3941 with a fresh database, and runs every test file against it: the API tests first, then the browser tests.

Browser tests

The browser tests drive Chromium through the pages: signing up and in, links and codes, two-factor, passkeys (with Chromium's virtual authenticator), organizations, single sign-on and the admin. Any console error or warning fails them, so a hydration mismatch does.

They use CHROMIUM_PATH, a system Chromium or Chrome, or Playwright's own (npx playwright install chromium). Without one, they're skipped.

Match toast messages exactly in browser tests: getByText('Saved', { exact: true }). Nuxt UI repeats a toast's words for screen readers, so a loose match finds two elements.

Adding a feature

Most features are one of Better Auth's plugins. A feature is done when it has:

  1. A module option in src/module.ts, off by default, documented with Better Auth's defaults, that adds the plugin to the server and client configs.
  2. Pages that read the set-up at runtime, from the capabilities route, so they also work when an app adds the plugin itself.
  3. Words in English and Spanish, in src/runtime/app/i18n/.
  4. A row on the admin's configuration page.
  5. Tests: an API test of what the server does, and a browser test when there's UI.
  6. A docs page, or a section on one, in docs/content/.

Ground rules

  • Better Auth does the work. Use its endpoints wherever it has them. The module's own routes are only for what Better Auth has no endpoint for, and each checks the same Better Auth permission the action needs.
  • Never change Better Auth's tables or replace its endpoints. The module's own data goes in its own tables, through Better Auth's migrations.
  • Rely on documented behaviour. Check Better Auth's docs for the version in use. When something only works through undocumented code (like the OAuth Popup plugin today), say so in the docs.
  • No patched dependencies. If a package needs patching to work, we replace it or work without it.
  • Server-render pages. Fetch page data with useFetch, and render dates with <NuxtTime>, so the server and the browser agree.

Commits and pull requests

  • Branch from main, and open your pull request against it.
  • Write commits as Conventional Commits: feat(sso): sign in by organization slug, fix(admin): …, docs: …, test(browser): ….
  • Explain what changed and why in the pull request, and how you tested it.

The docs

Terminal
pnpm docs:dev       # http://localhost:3002
pnpm docs:generate  # the static build, as GitHub Pages serves it

Pages are Markdown in docs/content/, with Nuxt UI's prose components. Options use ::field{name type default}. Pushes to main that change docs/ publish the site.

Screenshots

The screenshots come from the playground, in light and dark. Retake them after changing a page that's pictured:

Terminal
pnpm build && pnpm dev:build
pnpm docs:screenshots

The script (docs/scripts/screenshots.mjs) starts its own playground on port 3943 with a fresh database, so it never touches your dev server. It sets up an example organization, then saves docs/public/screenshots/<name>-<light|dark>.webp. Show one on a page with :screenshot{name="admin-home" alt="…"}.

License

By contributing, you agree that your contributions are licensed under the project's MIT license.