Evaluate in your application
Browser SDK
In a browser, the JavaScript SDK evaluates for one user, from a datafile anyone using the page can read.
Install#
npm install @featurevisor/sdkThe JavaScript SDK page is the reference for every method.
What users can see#
Anyone can open a browser datafile and read its conditions and values. Keep secrets, private customer data and the names of unannounced launches out of it, and use targets to ship only what the browser needs. A flag can hide a button, but the server must still check that the user is allowed to do what the button does.
Startup and cached datafiles#
Start from a datafile bundled with the application, so the first render never waits for the network. Then use a copy cached from the last visit, and fetch a fresh one in the background. This uses the refresh helper from the Node.js guide, saved as datafile-refresh.mjs:
import { createFeaturevisor } from '@featurevisor/sdk'import bundledDatafile from './datafile.json'import { refreshDatafile, waitForRefresh } from './datafile-refresh.mjs'const f = createFeaturevisor({ datafile: bundledDatafile })// One key per application, target, environment and cache formatconst cacheKey = 'storefront:all:v1'try { const cached = localStorage.getItem(cacheKey) if (cached) f.setDatafile(cached, true)} catch (error) { console.warn('Configuration cache unavailable:', error)}async function refreshAndCache() { try { const datafile = await refreshDatafile( f, '/datafiles/featurevisor-all.json', ) localStorage.setItem(cacheKey, JSON.stringify(datafile)) } catch (error) { console.warn('Refresh or cache write failed:', error) }}void refreshAndCache()- A datafile the SDK rejects, from the cache or the network, leaves the previous one in place. A storage error only skips the cache.
- A cached datafile can be older than the bundled one. Give the cache an expiry, or compare revisions before using it.
- Refresh from one place only, such as a timer or a push notification. When the page is done with the SDK, stop refreshing,
await waitForRefresh(f), then close it. AbortSignal.timeoutin the helper needs a recent browser. Replace it for older ones.- Each refresh replaces the whole datafile. To load several targets as they are needed, see Loading datafiles on demand.
Context for the user#
A browser instance serves one user, so set their context on the instance once you know who they are:
f.setContext({ userId: 'user-4', country: 'nl',})f.isEnabled('checkout')On a server, never do this on a shared instance. See Node.js.
Server to browser handoff#
When a server renders the page, it can evaluate for the request and send the results along, so the browser shows the same thing on its first render:
// On the server, after authentication. f is the shared instance.const context = { userId: String(request.user.id), country: request.user.country,}const bootstrap = { context, stickyFeatures: f.getFeatureEvaluations(context, [ 'checkout', ]), stickyVariables: f.getVariableEvaluations(context, [ 'supportEmail', ]),}Send bootstrap through your framework's way of passing data to the page, not by writing it into a script element by hand. In the browser, apply it before the first render:
f.setContext(bootstrap.context, true)f.setStickyFeatures(bootstrap.stickyFeatures, true)f.setStickyVariables(bootstrap.stickyVariables, true)Sticky values keep the server's results until you clear them. When the user changes or the handoff is stale, set the new context and clear both maps with f.setStickyFeatures({}, true) and f.setStickyVariables({}, true) before rendering again. Never let a page cache serve one user's handoff to another.
OpenFeature#
If your application uses OpenFeature, install the browser provider with the OpenFeature web SDK:
npm install \ @featurevisor/openfeature-provider-web \ @openfeature/web-sdkimport { OpenFeature } from '@openfeature/web-sdk'import { FeaturevisorOpenFeatureProvider,} from '@featurevisor/openfeature-provider-web'const provider = new FeaturevisorOpenFeatureProvider({ datafile,})await OpenFeature.setProviderAndWait(provider)await OpenFeature.setContext({ targetingKey: 'user-4', country: 'nl',})const client = OpenFeature.getClient()const enabled = client.getBooleanValue('checkout', false)The targeting key becomes userId, so Featurevisor evaluates user-4 in the Netherlands and enabled is true.
If an invalid datafile is given at the start, setProviderAndWait rejects with PARSE_ERROR, whatever the log level. A valid datafile passed later through provider.featurevisor.setDatafile() makes the provider ready again.
OpenFeature clients are told to evaluate again whenever the datafile, sticky values or Featurevisor context change, including through setContext on an instance you passed in. OpenFeature's own context is kept separately and wins for each evaluation.
Reuse an existing instance#
If your application already has an instance, pass it as featurevisor:
import { createFeaturevisor } from '@featurevisor/sdk'const featurevisor = createFeaturevisor({ datafile })const provider = new FeaturevisorOpenFeatureProvider({ featurevisor,})await OpenFeature.setProviderAndWait(provider)The provider borrows the instance, so your application still closes it once everything using it has finished. An instance you pass takes precedence over options such as datafile and modules.
Browser support#
The SDK's own code avoids recent browser APIs, so it runs in older browsers without polyfills. Code you add around it, such as the refresh helper, may need them.

