SvelteKit
This guide installs Auth UI in a SvelteKit 3 app with a Convex backend. jsrepo copies the components, routes and Convex functions from the Convex-Better-Auth-UI registry into your project, and you own them from then on. At the end you have password sign-in with every non-public page behind a session.
For a new app, follow every step. For an existing app, skip step 1 and read What the install replaces before step 2.
1. Create a SvelteKit app
This creates a SvelteKit 3 app with TypeScript, Tailwind CSS and the @tailwindcss/forms plugin. The Auth UI styles need both Tailwind packages, and jsrepo doesn’t install them.
In an existing app, you need SvelteKit 3: upgrade a SvelteKit 2 app with the SvelteKit 3 migration guide first, and if it already has Auth UI, follow Upgrade to SvelteKit 3 and Skeleton 5 instead of this guide. If Tailwind CSS isn’t set up yet, add it with the forms plugin:
2. Install Auth UI
Run the install from the project root. During jsrepo init @auth/svelte, press Enter to accept the default paths.
In a new app:
--all adds every registry item, --overwrite replaces existing files without asking, and --yes skips the confirmation. jsrepo installs the npm dependencies the items need.
In an existing app, commit your work first and leave out --overwrite --yes. jsrepo then asks before it changes a file, so you can keep yours and merge the parts Auth UI needs:
What the install replaces
These files already exist in most SvelteKit apps. With --overwrite, the install replaces them:
| File | Item | What Auth UI puts there |
|---|---|---|
vite.config.ts | base | tailwindcss() and sveltekit() with adapter-auto. Other plugins you had, such as Vitest or devtools-json, are dropped. |
src/app.html | base | data-theme="auth" on <html>, which activates the theme in src/themes/auth.css |
src/app.d.ts | base | Types for locals.token and the profile dialogs’ page state |
src/env.ts | base | Declares PUBLIC_CONVEX_URL and PUBLIC_CONVEX_SITE_URL. SvelteKit 3 only exposes declared variables. |
src/hooks.ts | base | Calls initConvex(PUBLIC_CONVEX_URL) |
src/hooks.server.ts | base/ssr | Route protection and the Convex session token |
src/routes/+layout.svelte | base | AuthProvider, the header with OrganizationSwitcher and UserButton, and the profile dialogs |
src/routes/+layout.server.ts | base/ssr | Preloads the user, accounts and organizations for server rendering |
src/routes/layout.css | base | Tailwind, Skeleton and the auth theme |
src/lib/assets/ | base | The favicon |
convex.json | base | Sets the Convex functions folder to src/convex/ |
src/convex/schema.ts, http.ts, convex.config.ts, auth.config.ts | base | The Better Auth component, its HTTP routes and the Resend webhook |
src/convex/auth.ts, auth.constants.ts | config | The Better Auth setup and the feature flags |
If your app already has Convex functions, convex.json moves the functions folder to src/convex/, and your schema.ts, http.ts and convex.config.ts need merging with Auth UI’s by hand.
Map the Convex imports
The components import your Convex code through #convex/.... jsrepo can’t edit package.json, so add the mapping next to the #lib entries that sv create wrote:
Without it, the build stops with Missing "#convex/_generated/api.js" specifier.
3. Start Convex
On its first run, npx convex dev logs you in, creates a project and a development deployment, and writes PUBLIC_CONVEX_URL and PUBLIC_CONVEX_SITE_URL to .env.local. These are the names src/env.ts declares.
Leave it running in its own terminal. It deploys your Convex functions on every change, and your app’s dev server doesn’t start it.
4. Set the Convex environment variables
In a second terminal, generate the secret Better Auth uses for encryption and hashing:
Allow your local hosts and set the fallback URL:
BETTER_AUTH_ALLOWED_HOSTS lists the hosts Better Auth accepts requests from and builds URLs for. BETTER_AUTH_FALLBACK_URL is used only when Better Auth runs without an incoming request, as in Convex queries, mutations, triggers, scheduled functions and device authorization.
Better Auth reads the protocol from the request and proxy headers. Cookies get the __Secure- prefix and the Secure attribute when BETTER_AUTH_FALLBACK_URL is an https URL, so the http URL here keeps them plain for local development. Keep it http there: Safari on older macOS doesn’t store Secure cookies from http://localhost. A deployment served over HTTPS with an http fallback URL sets BETTER_AUTH_BASE_URL_PROTOCOL="https", as the deploy guides do for previews.
5. Run the app
Open http://localhost:5173, click Sign in in the header, and create an account with an email and a password. The default auth.constants.ts turns on password sign-in only, and it works without any email setup.
What the install adds
Routes
| Route | File | Item |
|---|---|---|
/signin | src/routes/(auth)/signin/+page.svelte | auth/routes |
/reset-password | src/routes/(auth)/reset-password/+page.svelte | auth/routes |
/api/auth/* | src/routes/api/auth/[...all]/+server.ts | auth/routes |
/api/organization/accept-invitation/[invitationId] | src/routes/api/organization/accept-invitation/[invitationId]/+page.svelte | organizations/routes |
/device | src/routes/(auth)/device/+page.svelte | device-authorization/routes |
/admin | src/routes/admin/+page.svelte | admin/routes |
The components live in src/lib/<area>/ui/, for example src/lib/users/ui/UserButton.svelte, and the Convex functions in src/convex/.
Route protection
src/hooks.server.ts sends every request without a Better Auth session cookie to /signin?redirectTo=<path>. After signing in, the user lands back on that path. These paths are public:
Add your own public pages to that list. Invitation links and /device aren’t public, so a signed-out visitor signs in first and then continues. The hook only checks that the cookie exists, so keep authorization checks in your Convex functions.
Next steps
- Configure features: the
AUTH_CONSTANTSflags for email, social providers, organizations, API keys, device authorization and the admin dashboard. - Components: drop
<UserButton />or<OrganizationSwitcher />into your own layout. - Deploy to Vercel or Cloudflare.