Skip to main content

Entity & Page Context

Feature flags can target who is viewing a page (user context) and what is on the page (entity context). Both use the same filter model, but they answer different questions and are evaluated at different call sites.

User context vs entity context​

User contextEntity context
Built-in kindUser (identity, groups, claims)Named kinds you define (Order, Product, Account, …)
ScopeOne signed-in user per browser/sessionOne domain object per evaluation (row, card, detail page)
Set viasetContext() / server HttpContextPass the entity on each check or widget
Rollout / percentageYes — percentage is user-onlyNo — never "% of entities"
Dashboard catalog syncN/ATrusted/server SDKs only (startup registration)

User context drives rollouts, segments, User Claims, and percentage filters. Set it once per session:

await toggly.setContext({
identity: user.id,
groups: user.groups,
claims: { plan: user.plan },
});

Entity context drives Context Property filters against a specific instance — the order on this detail page, the product in this table row. Pass it per evaluation, not on global setContext():

// List page: each row can resolve differently, no extra network call
orders.map((order) =>
Toggly.isFeatureOn('OrderBadge', order, 'Order') ? <Badge /> : null
);
important

Entity context is per widget / per check, not process-wide. Do not attach entity attributes to global user setContext() — that would apply one entity to the whole app.

How evaluation combines user and entity rules​

For a feature with both user filters and Context Property filters:

  1. User filters (targeting, percentage, claims, browser, …) run on the worker when building evaluated-signed payloads, using the user's identity from the GET query string.
  2. If user filters fail → result is false (no gate object).
  3. If user filters pass and entity rules remain → result is an EntityGate object for offline client evaluation.
  4. If user filters pass and there are no entity rules → result is true.

On the client, the final boolean is:

enabled = userPassed (from server) AND entityRulesPass (local, when gate present)

Percentage / rollout is user-only. Entity rules never participate in percentage bucketing.

Mixed evaluated definitions (boolean | EntityGate)​

Client SDKs fetch evaluated-signed definitions as a map of feature key → value:

type EvaluatedDefinitionValue = boolean | EntityGate;

interface EntityGate {
requirement: 'all' | 'any';
rules: EntityGateRule[]; // property, op, value, optional type
}

type EvaluatedDefinitions = Record<string, EvaluatedDefinitionValue>;

Examples:

Server valueMeaning
falseOff for this user (user filters failed or feature disabled)
trueOn for this user; no entity rules to check locally
{ requirement, rules }User filters passed; evaluate rules locally against the entity you pass

Offline local evaluation​

After one evaluated-signed fetch, list pages evaluate entity gates locally — different rows can show different UI with no extra network per row. The worker pre-computes user-side filters; the client applies entity rules from the cached gate object.

Use resolveEvaluatedDefinition(value, entityContext) from @ops-ai/toggly-hooks-types (or your SDK wrapper):

import { resolveEvaluatedDefinition, registerContext } from '@ops-ai/toggly-hooks-types';

registerContext('Order', (order) => ({
kind: 'Order',
key: String(order.id),
attributes: { Status: order.status, Total: order.total },
}));

const enabled = resolveEvaluatedDefinition(
flags['OrderBadge'],
{ kind: 'Order', key: '7', attributes: { Status: 'red' } },
);

Fail closed without context​

Evaluation fails closed when:

  • The feature value is an EntityGate but you did not pass entity context
  • The entity kind is unknown (no registerContext mapper on client; unregistered type on server)
  • The mapper cannot produce attributes for the instance

Unknown or missing context → off, not "match any entity."

Old SDKs and truthy gate objects​

Legacy clients that check defs[key] === true treat gate objects as off. Gate objects are not truthy — always use resolveEvaluatedDefinition or SDK helpers that call it.

Schema mapping: registerContext vs startup registration​

Client SDKs — local registerContext only​

Browser and mobile SDKs do not call the dashboard Contexts API. Register mappers locally so the SDK can turn domain objects into { kind, key, attributes }:

Toggly.registerContext('Order', (order) => ({
kind: 'Order',
key: String(order.id),
attributes: {
Status: order.status,
Total: order.total,
},
}));

// Shorthand: pass entity + kind; SDK runs the mapper
Toggly.isFeatureOn('OrderBadge', order, 'Order');

You can also pass a fully built TogglyEntityContext object as the second argument.

Trusted server SDKs — startup registration (.NET / Node)​

Server SDKs register context schemas at startup (default on) so the Toggly dashboard Contexts page lists available kinds and properties for filter authoring:

services.AddTogglyEntityContext<Order>(
"Order",
p => p.Id.ToString(),
builder => builder
.KeyProperty("Id")
.Property("Status", "string")
.Property("Total", "number"));

Set RegisterContextsOnStartup = false in TogglySettings to disable catalog sync (evaluation still works; dashboard may not list the kind until registered manually).

Registration failures are logged only — they do not block app startup.

note

User traits on evaluated-signed GET (?u=, ?g=, claim.*) are not a separate entity evaluate POST. See Evaluated-signed responses.

List pages and detail pages​

Page typePattern
DetailOne entity instance → pass it to <Feature context={…}> or isFeatureOn(key, entity)
ListMap rows; each row passes its own entity; gates resolve from cached defs
{orders.map((order) => (
<Feature key={order.id} featureKey="ExpressCheckout" context={order} contextKind="Order">
<ExpressButton order={order} />
</Feature>
))}

SDK guides​

SDKGuide
.NET / RazorEntity context (.NET)
JavaScript (core)Vanilla JS — entity context
ReactReact — entity context
AngularAngular — entity context
VueVue — entity context
SvelteSvelte — entity context
AstroAstro — entity context
GatsbyGatsby — entity context
DocusaurusDocusaurus — entity context
Next.js clientNext.js client
Next.js serverNext.js server
Next.js edgeNext.js edge (boolean collapse)
Nuxt client / serverNuxt client, server
RemixRemix client, server
React NativeReact Native
FlutterFlutter
iOSiOS
AndroidAndroid
Node.jsNode.js — entity context
GoGo evaluation
JavaJava evaluation
PythonPython
RustRust evaluation
RubyRuby context
PHPPHP