Skip to content

Targeting & Attributes

Every flag evaluation runs against a context you own:

{ identity, attributes }
  • identity is opaque to Bridge — an email, internal UUID, anonymous ID, tenant ID, whatever your app uses. It powers deterministic bucketing (percentage rollouts) and observability.
  • attributes is a free-form key-value map. You choose what to send; anything you send can be targeted.

Anonymous evaluation is a first-class case — on the frontend the SDK manages an anonymous identity for you (see anonymous targeting below).

Two ways, and they compose:

Per-call context — for transient or call-site-specific values:

<script lang="ts">
import { useFlag } from '@nebulr-group/bridge-svelte/flags';
const checkout = useFlag('new_checkout', false, () => ({
attributes: { cart_size: cart.items.length, locale: navigator.language },
}));
</script>

App-state attributes — for values that live in your app state (current cohort, route segment, workspace), set them once and every eval picks them up:

bridge.attributes.set('cohort', '2026-q2');

On key collision, the value you supply always wins over anything Bridge contributes — and the admin UI marks the collision on the flag detail page so it’s never silently confusing.

There is no attribute registration step. The first time a key appears in your eval context, it self-announces to Bridge and shows up in the rule builder’s attribute picker — namespaced as custom: (yours) or bridge: (contributed by Bridge subsystems), with its inferred type and sample observed values inline.

Adding a new targeting dimension is a one-line code change.

Discovery is per environment: keys discovered in dev never appear in prod.

A flag is in one of three states:

StateMeaning
Off for everyoneKill switch — everyone gets the safe value
On for everyoneEveryone gets the on value
On for users matching a ruleRule decides, with explicit when matched / when not matched values

When you pick “matching a rule,” the builder appears inline on the flag’s page:

  • Conditions combine with AND inside a branch; multiple branches are first-match-wins.
  • Operators are type-awaregt/lt/between for numbers, contains/regex/in for strings, exists/not_exists for presence. The picker only offers operators valid for the attribute’s type. The full set: eq, neq, contains, not_contains, in, not_in, gt, lt, between, regex, exists, not_exists.
  • Percentage rollout is one input on the rule — “10% of matching users” — not a separate concept. Assignment is a deterministic hash of the flag key and identity, so a user stays in their bucket across sessions, devices, and server/client evaluations.
  • A live “currently matches X users” preview shows the rule’s footprint before you save, and a would match after save comparison shows blast radius when editing.

Rules built once can be saved as a reusable group and shared across flags — you’ll discover that option on the flag page the first time you need it.

Flags aren’t just booleans:

TypeUse caseExample
booleanFeature gatestrue / false
stringCopy variants, theme, mode selection"Pay now"
numberLimits, thresholds50
jsonStructured config{ window: 60, max: 100 }

One API for all of them — the type is inferred from your default value:

const isDark = useFlag('dark_mode', false); // boolean
const cta = useFlag('checkout_text', 'Submit'); // string
const limit = useFlag('max_uploads', 10); // number
const cfg = useFlag('rate_limit', { window: 60, max: 100 }); // JSON

If an admin changes a flag to a different type than your default suggests, the SDK returns your default and logs a warning — it never throws into your app.

On the frontend, the SDK generates an anonymous ID on first load and persists it, so anonymous visitors get stable bucketing across sessions — your marketing-page A/B test works before anyone signs up.

Choose how it’s stored at init, depending on your privacy posture:

ModeStorageSticky across sessions?
persistent (default)localStorageYes
sessionsessionStorageSame tab only
nonein-memoryPage lifetime only

When a visitor signs up or logs in, call identify(userId) — the SDK switches to the known identity and links prior anonymous activity to it, so attribution stays continuous across the login boundary.

On the backend there is no ambient identity: pass identity per eval (the user, or a tenant ID for sticky per-tenant behavior), or pass nothing for system-level flags. See common patterns for the rollout implications.