Framework guides
Astro
Evaluate in Astro pages as the server renders them, so the browser receives HTML and never the datafile.
Create an Astro project#
npm create astro@latestInstall the SDK#
npm install @featurevisor/sdkThe JavaScript SDK page is the reference for every method.
Share one instance on the server#
This example targets Astro's Node.js adapter. Install it with npx astro add node and enable on demand rendering for personalized pages. Prerendered pages evaluate once at build time, without a visitor's request context, so a flag change reaches them only when the site is built again.
Save the shared ES module refresh helper unchanged as src/datafile-refresh.mjs. Use only the helper, not the Node.js startup timer.
Create a module for the shared instance. Set FEATUREVISOR_DATAFILE_URL in the Node server's environment:
import { createFeaturevisor } from '@featurevisor/sdk'import { refreshDatafile } from './datafile-refresh.mjs'const instance = createFeaturevisor({})let ready = falselet nextRefreshAt = 0export async function getInstance() { const datafileUrl = process.env.FEATUREVISOR_DATAFILE_URL if (!datafileUrl) throw new Error('Set FEATUREVISOR_DATAFILE_URL') if (Date.now() >= nextRefreshAt) { try { await refreshDatafile(instance, datafileUrl) ready = true nextRefreshAt = Date.now() + 5 * 60 * 1000 } catch (error) { if (!ready) throw error nextRefreshAt = Date.now() + 30 * 1000 console.error('Configuration refresh failed:', error) } } return instance}Import only from server code. Each worker waits for its first valid datafile, then refreshes on requests after five minutes. Failures retain the previous datafile and retry after thirty seconds. Keep one fixed URL per instance.
Requests drive the refresh, so no timer is needed.
Evaluate in a page#
Every visitor needs a stable ID to be bucketed by. This page gives visitors who are not signed in a random ID in a cookie on their first visit, and uses it as userId:
---import { getInstance } from '../featurevisor.mjs'export const prerender = falseAstro.response.headers.set('Cache-Control', 'private, no-store')let userId = Astro.cookies.get('visitorId')?.valueif (!userId) { userId = crypto.randomUUID() Astro.cookies.set('visitorId', userId, { path: '/', httpOnly: true, sameSite: 'lax', secure: import.meta.env.PROD, maxAge: 60 * 60 * 24 * 365, })}const f = await getInstance()const context = { userId,}const newCheckout = f.isEnabled('checkout', context)---{newCheckout ? <NewCheckout /> : <Checkout />}- The visitor can change the cookie, so it is good for bucketing and nothing else. For signed in users, use the ID your authentication middleware verified, from
Astro.locals. - Use the same ID wherever the visitor is evaluated, on the server and in the browser. Never fall back to one ID shared by every visitor, which would put them all in one bucket.
- Pass context to each evaluation. Never call
setContext()on the shared instance. - Keep personalized HTML out of shared caches, as the
Cache-Controlheader does here.
The Astro cookie API lists the cookie options.
Related#
- React and Vue.js, for components rendered in the browser
- Deployment
- Example application

