Skip to content

Browser SDK

@lucerna-dev/gates-browser shows and hides UI with flags, kill switches and experiments. It fetches decisions for the current user once — your targeting rules never reach the browser — and answers every check instantly from memory.

Setup

ts
import { createGates } from "@lucerna-dev/gates-browser";
import { createIdentity } from "@lucerna-dev/identity";

export const identity = createIdentity({ apiKey: "ck_client_prod_…" });

export const gates = createGates({
  clientKey: "ck_client_prod_…",
  identity,
});

Options

OptionDefaultWhat it does
clientKeyYour public client key (ck_client_…). Required.
identityThe current user. Decisions refetch whenever it changes.
baseUrlhttps://api.uselucerna.appOverride for local development.
storagee.g. localStorage — caches decisions for instant next load.
storageKeylucerna:gatesCache entry name.
requestTimeoutMs5000Timeout per fetch.
onErrorCalled when a fetch fails. Wire it to your logging.
fetchglobal fetchCustom transport, mostly for tests.

Checking gates

Checks take no arguments — they answer for the bound identity:

MethodReturnsIf unknown or not loaded
gates.flag(key)booleanfalse
gates.experiment(key)variant name or nullnull
gates.switch(key)booleanfalse means killedtrue (not killed)
gates.decisions()the full snapshotundefined

When decisions update

  • On identity changeidentify(), trait() or reset() trigger one refetch (bursts are batched). Switching to a different user clears the old decisions immediately — no flash of someone else's UI.
  • On gates.refresh() — call it yourself when it matters, e.g. after checkout.

There is no background poll in the browser. A dashboard change is picked up on the next page load, identity change or refresh().

Caching

Pass storage: localStorage and the last decisions render instantly on the next page load while fresh ones fetch. The cache is keyed to the user it was computed for — another user (or a new anonymous id) starts clean.

Frameworks

Everything React lives under the /react subpath:

tsx
import {
  GatesProvider,
  Feature,
  KillSwitch,
  Experiment,
  Variant,
  useFlag,
  useExperiment,
  useKillSwitch,
  useGates,
} from "@lucerna-dev/gates-browser/react";
  • <GatesProvider client={gates}> — once, at the root.
  • <Feature name fallback> — renders children while the flag is on.
  • <KillSwitch name fallback> — children while the path is alive, fallback when killed.
  • <Experiment name fallback> + <Variant name> — renders the assigned variant; fallback when not in the experiment.
  • useFlag(key) / useExperiment(key) / useKillSwitch(key) — re-render when decisions change.
  • useGates() — the client itself, for refresh().

Full component semantics, SSR notes and per-framework setup live in the framework guides: React, Astro, Preact.

Devtools

An in-page widget for development — live decisions with plain-language evaluation explanations, local override toggles, identity impersonation — with zero bytes in production builds. See Devtools.

When things go wrong

Reads never throw. Before the first load — or if a refetch fails — checks answer safe defaults (false / null / not killed) and keep serving the last decisions they have. ready() rejects on a bad client key; onError fires on every failed fetch.

UX only

Browser decisions show and hide UI. Authorization lives on your backend — verify with the Node SDK or your own checks.

Lucerna Developer Docs