Web quickstart
In a few minutes your website will read a real feature flag — with a durable cache, a fail-safe answer for every situation, and nothing for you to manage in the browser.
Zero runtime dependencies; the whole SDK rides under a 10 kB gzip ceiling.
1. Install
Section titled “1. Install”The SDK is pre-release and isn’t published to npm yet. If your team has been given access to the repository (
FortressFlag_SDK_web), build it withpnpm install && pnpm buildand consume the ESM output indist/— the import below is the package’s entry point.
2. Start the SDK
Section titled “2. Start the SDK”Once, early in your page or app bootstrap:
import { Environment, FortressFlag } from "@fortressflag/sdk-web";
FortressFlag.start({ sdkKey: "ffc_prod_…", // from your project's SDK keys screen environment: Environment.PRODUCTION,});start returns immediately; cached values from previous visits are available right away,
and fresh values arrive in the background. The key is a client key — it’s
visible in your page source by design.
3. Read a flag
Section titled “3. Read a flag”if (FortressFlag.isEnabled("new-checkout", false)) { // render the new checkout}stringValue and numberValue read multivariate flags the same way, and
FortressFlag.onChange tells you when values move so you can re-render.
That’s it — your flag is live. Toggle it in the dashboard and open the page again (or wait out a poll) to watch it follow.
Nothing here ever throws
Section titled “Nothing here ever throws”No call in this API throws, and no promise from it rejects. Every read resolves through the
cascade: the freshest fetched value, then the last value this browser recorded, then your
default, then false. A FortressFlag outage, a dead network, an ad blocker, a revoked key —
all of them resolve to a flag value, none of them reach your code as an error.
(That ad-blocker case is real: some privacy extensions block flag requests. Cached values keep serving, and a browser that never fetched serves your defaults — “flags don’t update with uBlock on” is the cascade working, not a bug.)
What lives in the browser, honestly
Section titled “What lives in the browser, honestly”- The device identity lives in
localStorage, scoped to your origin. It’s random-minted and pseudonymous — and readable by your page’s own JavaScript, the same class of value as the SDK key beside it. - Private windows mint throwaway identities. Each private session counts as a device while it lasts; a user clearing site data is a new device too. And one person on your website, iOS app, and Android app is up to three devices — there’s no cross-platform linkage, by design.
- Your test suites are free. Browsers driven by Playwright, Selenium, Puppeteer, or
Cypress declare themselves, and the SDK mints a
sim_identity there: served flags, never billed. - Version tags don’t exist on web. The SDK sends
platformandsdkVersionautomatically, but there’s no reliableappVersionorosVersionfor a page — a targeting rule on those simply never matches a browser.
Where to next
Section titled “Where to next”- How flags reach your app — polling and the cache
- Device identity and billing — the identity in that storage key
- Targeting — rules, segments, and rollouts