Skip to content

The options you pass to bridgeBootstrap — and the VITE_BRIDGE_* variables it reads — control how Bridge wires up auth, routing, and billing in your app. See all config options for the full list.

Export load = bridgeBootstrap({ … }) from your root +layout.ts. The app ID comes from Control Center (your admin dashboard at app.thebridge.dev): open your app’s settings and copy its ID into your .env as VITE_BRIDGE_APP_ID — Bridge reads it from there (see Environment variables).

// src/routes/+layout.ts
import { bridgeBootstrap } from '@nebulr-group/bridge-svelte';

export const ssr = false;

export const load = bridgeBootstrap({
  loginRoute: '/auth/login',
  rules: [
    { match: '/', public: true },
    { match: new RegExp('^/auth($|/)'), public: true },
  ],
  defaultAccess: 'protected',
});

Signature:

bridgeBootstrap(
  options?: Partial<BridgeConfig> & Partial<RouteGuardConfig>
): (event: { url: URL; fetch: typeof fetch }) => Promise<{ config: BridgeConfig; routeConfig: RouteGuardConfig }>

Every option is optional. Precedence: an option you pass explicitly wins over the environment, and the environment wins over the built-in default. The returned function is your layout’s load; the data it returns (config, routeConfig) is available to every page as page.data.

The older positional form, await bridgeBootstrap(url, config, routeConfig, fetch) inside a hand-written load, still works but is deprecated and reads no environment variables.

SvelteKit re-runs the root layout load on every navigation, so bridgeBootstrap’s load is called many times per page load. The one-time setup (applying the config, patching fetch, refreshing the token, loading billing state and warming the flag cache) runs once per page load, on the first call. The route guard runs on every call, so each navigation is checked. See Route guards.

Read the active config at runtime via the readonlyConfig store:

<script lang="ts">
  import { readonlyConfig } from '@nebulr-group/bridge-svelte';

  const config = $derived($readonlyConfig);
</script>

{#if config}
  <p>App ID: {config.appId}</p>
  <p>Login route: {config.loginRoute}</p>
{/if}

callbackUrl is the URL Bridge calls back to once a hosted or SSO login completes. If you omit it, the SDK uses ${window.location.origin}/auth/oauth-callback, so that route file must exist (it can be empty; bridgeBootstrap handles the callback in your root layout load before the page renders) and must be public.

The same URL is where Stripe returns after checkout. <PlanSelector> sends Stripe callbackUrl with ?stripe_success=1&session_id=… (or ?stripe_cancel=1) plus the destination, and bridgeBootstrap confirms the payment, refreshes the token and redirects to the selector’s successRedirect / cancelRedirect (default /subscription), or to billing.paymentErrorRoute (default /subscription/error) if confirmation fails.

Passing a specific callbackUrl lets you send different parts of your app through different post-login destinations, for example an admin section and a regular user section of the same app, or entirely separate apps sharing one Bridge project.

Whatever you pass here, and the default above, must be registered as an allowed redirect URI in Control Center (see Configs managed in Control Center); Bridge only redirects to callback URLs it’s been told about. The app’s Default callback URL setting there is only used by the Bridge server when a login request carries no callback URL at all, which the SDK never does.

export const load = bridgeBootstrap({
  callbackUrl: `${window.location.origin}/admin/oauth-callback`,
  rules: [ /* … */ ],
});

Two options point the SDK at Bridge itself. Both default to production, so a production app on the standard cloud can leave them out. Any other app must set the API address, as VITE_BRIDGE_API_BASE_URL in .env: a stage, local or self-hosted app ID doesn’t exist on the production API, and requests fail with “Not Found”. The hosted address follows the API address on Bridge’s own domains (api-stage.thebridge.dev → auth-stage.thebridge.dev), so only a local or self-hosted Bridge also sets VITE_BRIDGE_HOSTED_URL; a development build warns when it cannot be derived. In a development build Bridge warns once in the console when it falls back to production because VITE_BRIDGE_API_BASE_URL is unset.

  • apiBaseUrl (default https://api.thebridge.dev): the base URL for the Bridge API. Every API endpoint the SDK calls is derived from it.
  • hostedUrl (default https://auth.thebridge.dev): the base URL for Bridge’s hosted UI, such as the hosted login page and plan selection. Only needed with hosted auth.

If you set loginRoute, unauthenticated users who hit a protected route are redirected to that in-app route, where your own login page (for example, one built with LoginForm) takes over.

If you leave loginRoute unset, Bridge uses hosted auth instead: unauthenticated users are redirected to Bridge’s hosted login page (served from hostedUrl). Unset is the default, so hosted login is what you get out of the box.

OptionTypeDefaultDescription
appIdstringVITE_BRIDGE_APP_ID (required)Your Bridge app ID, found in your app’s settings in Control Center. With none anywhere, Bridge refuses to start and names the variable
apiBaseUrlstringVITE_BRIDGE_API_BASE_URL, else 'https://api.thebridge.dev'Base URL for the Bridge API; all endpoints are derived from it. Required for any non-production app. See Base URLs
hostedUrlstringVITE_BRIDGE_HOSTED_URL, else derived from apiBaseUrl on Bridge’s own domains, else 'https://auth.thebridge.dev'Base URL for Bridge’s hosted UI (login page, plan selection). See Base URLs
callbackUrlstring${origin}/auth/oauth-callbackWhere hosted/SSO login and Stripe checkout return to. See Callback URL
defaultRedirectRoutestring'/'Accepted, but bridge-svelte does not currently read it: after hosted login the user returns to the page they asked for, or /; in SDK mode your LoginForm’s onLogin decides
loginRoutestring(unset)In-app route of your login page. Leave unset for hosted auth: without it, unauthenticated users go to Bridge’s hosted login page. See Login route
signupRoutestring'/auth/signup'Route where your signup page lives; LoginForm’s signup link points here unless its signupHref prop overrides it
localestring'en'UI language of the SDK auth components, e.g. 'sv'. Unknown locales fall back to English
messagesMessageOverrides(none)Per-key copy overrides on top of the locale
devBadgebooleantrueShow the “Live updates off — why?” badge in development builds. Never shown in production builds. See Live updates
billing.paywallRoutestring | false'/subscription/plan'Where a signed-in workspace (called a tenant in the API) with no plan is redirected, before any page renders. The default applies only when the app has plans; a value you set always applies. false turns the redirect off
billing.paymentErrorRoutestring'/subscription/error'Where a failed Stripe checkout confirmation lands
billing.manageRoutestring'/subscription'The subscription page; where the Upgrade/Manage buttons in <BridgeQuotaBanner> and <BridgeBillingNotice> point

The three billing defaults are pages <BridgeBillingRoutes /> serves from src/routes/subscription/[...bridge]/+page.svelte; see Add billing to your app. | storage | TokenStorage | localStorage (browser) / memory (SSR) | Token storage adapter; implement get/set/remove to bring your own | | debug | boolean | VITE_BRIDGE_DEBUG === 'true', else false | Enable debug logging |

The rules, defaultAccess and returnTo options of bridgeBootstrap declare which routes are public, protected, flag-gated, or billing-gated:

interface RouteGuardConfig {
  rules: RouteRule[];
  /** Access for routes no rule matches. @default 'protected' */
  defaultAccess?: 'public' | 'protected';
  /** Deep-link preservation across login. On by default. */
  returnTo?: ReturnToConfig;
}

interface ReturnToConfig {
  /** Set false to stop carrying the attempted page across login, so
   *  every login lands on your default route. @default true */
  enabled?: boolean;
  /** Query parameter carrying the attempted path in SDK mode.
   *  @default 'redirectUri' */
  param?: string;
  /** Paths that must never become a return target, on top of your
   *  loginRoute (always excluded). Same match forms as RouteRule.match. */
  exclude?: (string | RegExp)[];
  /** Your login route. Filled in from BridgeConfig.loginRoute — only set
   *  this if you call createRouteGuard directly. */
  loginRoute?: string;
}

interface RouteRule {
  /** Path to match: exact string or RegExp. */
  match: string | RegExp;
  /** Route is accessible without authentication. */
  public?: boolean;
  /** Require feature flag(s): a key, { any: [...] }, or { all: [...] }. */
  featureFlag?: string | { any: string[] } | { all: string[] };
  /** Where to send users who fail the featureFlag requirement. @default '/' */
  redirectTo?: string;
  /** Billing gate. 'soft' (default) renders the route and relies on the
   *  in-app notice; 'hard' redirects a billing-locked workspace to its
   *  recovery URL. */
  billing?: 'soft' | 'hard';
}

See Route guards for a walkthrough, including returning to the attempted page.

Bridge reads these from your .env (the VITE_ prefix is required for values to reach the browser). You do not pass them anywhere; an option passed to bridgeBootstrap() explicitly still wins over them.

VariableOption
VITE_BRIDGE_APP_IDappId
VITE_BRIDGE_API_BASE_URLapiBaseUrl
VITE_BRIDGE_HOSTED_URLhostedUrl
VITE_BRIDGE_DEBUGdebug ('true' turns it on)
VITE_BRIDGE_APP_ID=your-app-id-here
# Only for a non-production app (stage, local, self-hosted):
VITE_BRIDGE_API_BASE_URL=https://api-stage.thebridge.dev

An empty value counts as unset.

Some settings aren’t passed in code at all. They’re set once per app, and Bridge enforces them server-side:

SettingWhat it does
Redirect URIsThe allowlist of callback URLs Bridge is allowed to redirect to. Any callbackUrl you pass to bridgeBootstrap must already be on this list.
Allowed originsThe origins (scheme, host and port) allowed to call the Bridge API from the browser. It is also enforced on in-app sign-in: from an origin not on the list, sign-in itself (password sign-in, and the token exchange that finishes magic-link, passkey and MFA sign-in), signup and passkeys answer 403 {"message":"Origin not allowed"}. It also decides where a magic link may be delivered: Bridge refuses to send one whose return page is on an origin that isn’t listed. In Control Center: Authentication → Security tab → Allowed Origins.
Default callback URLUsed whenever your app doesn’t pass a callbackUrl in code. See Callback URL.
  • CLI:

    bridge app update \
      --redirect-uris "https://app.example.com/oauth-callback,https://admin.example.com/oauth-callback" \
      --allowed-origins "https://app.example.com,https://admin.example.com" \
      --default-callback-uri "https://app.example.com/oauth-callback"
    
  • Control Center: the same settings, managed from your app’s settings.

  • MCP (AI-assistant integration): connect your AI assistant to https://api.thebridge.dev/mcp as a remote MCP server (sign in and approve access to your app in the browser when it asks). Its add_redirect_uri and remove_redirect_uri tools change redirect URIs one at a time, and update_app sets allowed origins (the list you pass replaces the whole list) and the default callback URL.