Featurevisor

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:

MicrofrontendActivitiesPathAccess
productsBrowse products/productsEveryone
signupSign up/signupAnonymous users
signinSign in/signinAnonymous users
checkoutBuy products/checkoutEveryone
accountManage account/accountAuthenticated 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:

featurevisor.config.js
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:

targets/products.yml
description: Products microfrontend
tag: products
targets/checkout.yml
description: Checkout microfrontend
tag: checkout

Repeat for signup, signin and account. Each target produces its own datafile, so every microfrontend gets a smaller datafile containing only the features it needs.

One build produces a datafile for each microfrontend: products, signup, signin, checkout and account.

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.

features/showMarketingBanner.yml
description: Shows marketing banner at the bottom of the page
tags:
- products
bucketBy: deviceId
rules:
- key: everyone
conditions: "*"
percentage: 100

The 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:

features/showMarketingBanner.yml
description: Shows marketing banner at the bottom of the page
tags:
- 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:

Command
npx featurevisor build

Each target gets its own datafile:

Command
tree datafiles
Output
datafiles
├── featurevisor-account.json
├── featurevisor-checkout.json
├── featurevisor-products.json
├── featurevisor-signin.json
├── featurevisor-signup.json
└── REVISION

Bucket 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:

MicrofrontendAccessbucketBy
productsEveryone?
signupAnonymous usersdeviceId
signinAnonymous usersdeviceId
checkoutEveryone?
accountAuthenticateduserId

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:

features/showMarketingBanner.yml
description: Shows marketing banner at the bottom of the page
tags:
- products
- checkout
bucketBy:
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:

products-microfrontend/index.js
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.

Previous
RBAC