Use cases
Microfrontends
Microfrontends let separate teams build and deploy parts of one frontend, with tools such as Module Federation, single-spa or Piral. With Featurevisor, every team's features live in one project, each microfrontend loads a datafile with only its own, and a user lands in the same bucket in every part.
The application#
Take a shop split into five microfrontends, each owning a path:
| Microfrontend | Activities | Path | Access |
|---|---|---|---|
products | Browse products | /products | Everyone |
signup | Sign up | /signup | Anonymous users |
signin | Sign in | /signin | Anonymous users |
checkout | Buy products | /checkout | Everyone |
account | Manage account | /account | Authenticated users |
In a larger application, a microfrontend might own a group of routes, or render components on a page another one owns. The setup below works the same.
Tag features by microfrontend#
Keep every feature in one project, and build a separate datafile for each microfrontend, so each loads only what it needs. Declare the tags that group features by microfrontend in the project configuration, preserving your other settings:
module.exports = { tags: [ 'products', 'signup', 'signin', 'checkout', 'account', ],}A target per microfrontend#
Tags group features, but the datafiles themselves are built from targets. Define one target per microfrontend, each selecting the matching tag:
description: Products microfrontendtag: productsdescription: Checkout microfrontendtag: checkoutRepeat for signup, signin and account. Each target produces its own datafile, so every microfrontend gets a smaller datafile containing only the features it needs.

Share a feature between two#
A showMarketingBanner flag toggles a banner at the bottom of a page. The default project already declares the userId and deviceId attributes it buckets by.
description: Shows marketing banner at the bottom of the pagetags: - productsbucketBy: deviceIdrules: - key: everyone conditions: "*" percentage: 100The products tag includes this feature in the products target. To show the banner in both products and checkout, assign both tags, retaining the bucketing and rules above:
description: Shows marketing banner at the bottom of the pagetags: - products - checkout# ...Build the datafiles#
If you started from the default scaffold, its targets/all.yml still includes every active definition. Keep it for consumers that need everything, or remove it when every consumer uses one of these filtered targets. The output below assumes it has been removed.
Once tags and targets are in place, build the datafiles:
npx featurevisor buildEach target gets its own datafile:
tree datafilesdatafiles├── featurevisor-account.json├── featurevisor-checkout.json├── featurevisor-products.json├── featurevisor-signin.json├── featurevisor-signup.json└── REVISIONBucket signed in users and visitors alike#
A feature's bucketBy decides which ID places a user in a bucket. That is simple when a microfrontend serves only signed in users, or only visitors who are not:
| Microfrontend | Access | bucketBy |
|---|---|---|
products | Everyone | ? |
signup | Anonymous users | deviceId |
signin | Anonymous users | deviceId |
checkout | Everyone | ? |
account | Authenticated | userId |
products and checkout serve both. Bucketing everyone by deviceId works, but a signed in user then gets a different result on each device. To follow signed in users across devices, bucket by the first of userId and deviceId that the context has:
description: Shows marketing banner at the bottom of the pagetags: - products - checkoutbucketBy: or: - userId - deviceId# ...A user who signs in may change bucket once, as their ID changes from the device's to their own. Every microfrontend that passes the same IDs gives them the same result.
Review changes in one place#
Because every team's features are in one repository, a change that affects several microfrontends is one pull request. Keep each team's features in its own namespace and name the team for that directory in CODEOWNERS, and the pull request asks every team it touches to review it.
Load the datafiles#
Once the datafiles are deployed, each microfrontend loads its own. There are two ways to do it.
One instance per microfrontend#
Each microfrontend creates its own SDK instance with its own datafile:
import { createFeaturevisor } from '@featurevisor/sdk'const DATAFILE_URL = 'https://cdn.example.com/featurevisor-products.json'const response = await fetch(DATAFILE_URL)if (!response.ok) throw new Error(`HTTP ${response.status}`)const datafileContent = await response.json()const f = createFeaturevisor({ datafile: datafileContent,})Evaluate your features:
const context = { deviceId: storedDeviceId, userId: signedInUser?.id,}if (f.isEnabled('showMarketingBanner', context)) { // render the banner}Pass userId only when someone is signed in.
Each microfrontend stays fully isolated, which suits ones built and deployed as separate applications that share nothing at runtime.
One shared instance, loaded on demand#
If your microfrontends share a host application, use one SDK instance and merge target datafiles as users visit each area. Load targets from the same immutable release and refresh the loaded targets together so shared dependencies stay consistent. Follow Loading datafiles on demand for the complete loader, HTTP checks and refresh flow.
With React or Vue, the host can wrap each microfrontend in a scope that names it, so a rule can target where a feature appears, such as keeping showMarketingBanner out of checkout. See Scopes for React and Vue.
Related#
- Targets for application specific datafiles
- Loading datafiles on demand for loading and refreshing multiple targets
- Bucketing for consistent allocation across applications

