Get started
Quickstart
On this page
1. Create a workspace and a project
Create an account, confirm your e-mail address, and enter a workspace name (usually your company or team) and a first project name. Joining an existing workspace instead? Ask its admin to invite your e-mail address; the invitation is accepted automatically when you sign up.
Every new project starts with three environments:
- Development and Staging: publish directly.
- Production: protected (only an owner can relax its publishing policy). On Team and Scale it is created requiring a reviewed change with an independent approval and passing scenario checks; on Explore required approvals and scenario checks are not available, so Production publishes directly, like the others.
2. Pick an environment
A flag has one draft configuration per environment, and each environment publishes its own numbered revisions. Use Development for this guide. Admins can add environments under Settings → Environments.
3. Create a flag
Open Flags → New flag, choose a key (for example checkout-v2) and a type (boolean, string, number or json). The key and the type cannot be changed later. Mark the flag client-exposed if browser code needs to read it: browser keys receive only client-exposed flags.
In the Development draft, turn the flag on and, for example, add a rule that serves true to 10 % of users. Saving changes the draft only; SDKs do not see it until you publish.
4. Create an SDK key
Under Settings → SDK & access keys (admin role), create a key for the Development environment. A key reads exactly one environment. The full key is shown once; NanoFlags stores only a hash. Put it in your secret manager.
- Server (
nf_srv_…): full configuration for trusted backends. A secret. - Client (
nf_pub_…): browser-safe, receives only client-exposed flags. Anyone with the page can read it.
CI (nf_ci_…) and admin (nf_adm_…) keys are for pipelines and the CLI and are refused by the SDK endpoints. See Keys and scopes.
5. Install the SDK
npm install @nanoflags/sdkOne package covers Node, browsers and edge runtimes (@nanoflags/sdk), React (@nanoflags/sdk/react) and Node test-session propagation (@nanoflags/sdk/node). ESM with TypeScript types. The live socket needs a global WebSocket (Node 22 or newer, browsers, Deno, Bun); otherwise the SDK falls back to server-sent events and polling.
6. Evaluate with an explicit fallback
Every accessor takes a fallback: the value your application uses when NanoFlags cannot answer (no configuration loaded yet, unknown flag, wrong type). Choose the fallback that is safe for your users, usually the current behaviour.
Node
import { createClient } from "@nanoflags/sdk";
const flags = createClient({
sdkKey: process.env.NANOFLAGS_SDK_KEY!, // nf_srv_…
baseUrl: "https://edge.nanoflags.io",
service: "checkout-api", // shown as the service that reports a revision
});
await flags.ready(); // never rejects; check flags.status
const enabled = flags.boolean("checkout-v2", false, { key: user.id, signedIn: true, region: "US" });Create one client per process and reuse it. Call flags.close() on shutdown: the live connection keeps a Node process alive until then. In short-lived scripts and serverless functions call close() or pass streaming: false.
Browser
import { createClient } from "@nanoflags/sdk";
export const flags = createClient({
sdkKey: "nf_pub_…", // client key: only client-exposed flags are delivered
baseUrl: "https://edge.nanoflags.io",
service: "web",
});
await flags.ready();
const ranking = flags.string("search-ranking", "relevance-v2", { key: user.id });nf_srv_ key to a browser. Rules of client-exposed flags are visible to anyone who has the client key, and evaluation runs on the user's device: do not use browser flags for authorization.React
import { createClient } from "@nanoflags/sdk";
import { NanoFlagsProvider, useFlag, useFlagsStatus } from "@nanoflags/sdk/react";
const client = createClient({ sdkKey: "nf_pub_…", baseUrl: "https://edge.nanoflags.io", service: "web" });
export function App({ user }: { user: { id: string; plan: string } }) {
return (
<NanoFlagsProvider client={client} context={{ key: user.id, plan: user.plan }}>
<Checkout />
</NanoFlagsProvider>
);
}
function Checkout() {
const v2 = useFlag("checkout-v2", false); // boolean accessor, chosen from the fallback's type
const { status, revision } = useFlagsStatus();
return v2 ? <NewCheckout /> : <OldCheckout />;
}The context attribute key is the stable identity used for percentage rollouts. Pass it for every evaluation that can hit a rollout; without it the context is treated as outside the allocation.
7. Publish
Back in the dashboard, open Publish draft for Development. The review shows the difference between the published revision and the draft. Publishing snapshots the whole draft into an immutable revision (r1, r2, …). If someone else published in the meantime, the publish is refused with Destination changed instead of overwriting their revision.
On Team and Scale, Production requires a change with an independent approval and passing scenarios for the same step: see Changes and approvals.
8. See it arrive
Connected SDKs receive the new revision over the live socket, without polling. Listen for it:
flags.on("update", ({ revision, previousRevision, transport, latencyMs }) => {
console.log(`now on r${revision} (was r${previousRevision}) via ${transport}`, latencyMs);
});
flags.on("error", (e) => console.warn(e.code, e.message)); // the last bundle keeps servingtransport is "live" when the revision was pushed, "stream" when it was fetched after a server-sent event, and "poll" otherwise. Current propagation measurements are published on the status page.
In the dashboard, the welcome page (/app/welcome) → Check connection shows the revision the service last reported. A report means one process using this key had that revision loaded when it reported; it does not prove that every instance did. See what a reported signal proves.
Next steps
- Concepts: drafts, revisions, changes, scenarios, segments and rollout bucketing.
- SDK reference: options, statuses, transports, persistence and telemetry.
- CI: run scenario checks on every pull request.