@lwrjs/gate-module-provider
A module provider for LWR that exposes @salesforce/gate for feature gating. Use it to control feature rollouts, experiment toggles, and gradual releases within your LWC modules.
Overview
The gate module provider resolves imports like @salesforce/gate/<gateName> into virtual modules that expose a standard API: isOpen(map) and hasError(). Each gate is resolved at build/runtime from a configurable source—by default, a gates.json file in your project.
Installation
The provider is included in the default LWR config. To customize it, add it to moduleProviders in your lwr.config.json:
{
"moduleProviders": [
[
"@lwrjs/gate-module-provider",
{
"gateConfigPath": "$rootDir/src/gates.json"
}
]
]
}
Options
| Option | Type | Default | Description |
|---|---|---|---|
gateConfigPath |
string |
$rootDir/src/gates.json |
Path to your gates config file. Supports $rootDir placeholder. |
resolver |
GateResolver |
— | Custom resolver function. Overrides gateConfigPath when provided. Use for container-specific resolution (e.g., Core, metadata). |
gates.json Format
Create a gates.json file (typically in src/) with an array of gate entries:
[
{
"name": "myFeatureGate",
"enabled": true,
"description": "Controls access to the new feature"
},
{
"name": "experimentPreview",
"enabled": false,
"description": "Preview mode for A/B test"
}
]
Gate Entry Fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Gate identifier. Must match the specifier (e.g. myFeatureGate → @salesforce/gate/myFeatureGate). |
enabled |
boolean |
Yes | Open/closed state. |
description |
string |
No | Human-readable description. |
Gate names may use alphanumeric characters, dots, underscores, and hyphens (e.g. myFeatureGate, namespace.gateName).
Unknown Gates
If a gate name is not found in gates.json, the resolver returns { isOpen: false, hasError: false }. Unknown gates are indistinguishable from known closed gates—both resolve as closed with no error. Ensure gate names in your imports match the name field in gates.json exactly.
Client API
Each gate module exports two functions:
isOpen(map)
Returns whether the gate is open.
- Resolved: Returns
trueorfalsebased on the gate config. - Error: Returns
map.fallbackwhenhasError()is true. Ifmapis undefined or lacksfallback, returnsfalse. - Defensive: Safe to call
isOpen()with no args; returnsfalsewhen resolution failed and no fallback was provided.
isOpen({ fallback: false }); // Use false when resolution fails
isOpen({ fallback: true }); // Use true when resolution fails (opt-in to feature on error)
isOpen(); // Safe: returns false when hasError() and no fallback provided
hasError()
Returns true when gate resolution failed (e.g. config missing, network error). Use this to handle error states explicitly.
Usage in LWC Modules
Basic Gating
// myComponent.ts
import { LightningElement } from 'lwc';
import { isOpen, hasError } from '@salesforce/gate/myFeatureGate';
export default class MyComponent extends LightningElement {
get featureEnabled(): boolean {
return isOpen({ fallback: false });
}
get showError(): boolean {
return hasError();
}
}
<!-- myComponent.html -->
<template>
<template lwc:if="{showError}">
<p class="error">Unable to check feature availability.</p>
</template>
<template lwc:elseif="{featureEnabled}">
<p>New feature content</p>
</template>
<template lwc:else>
<p>Feature not available</p>
</template>
</template>
Conditional Rendering
import { isOpen } from '@salesforce/gate/experimentPreview';
export default class Dashboard extends LightningElement {
get showNewDashboard(): boolean {
return isOpen({ fallback: false });
}
}
Safe Defaults
When resolution fails, isOpen returns your fallback value. Choose based on your use case:
fallback: false— Feature off when resolution fails (safer for new features).fallback: true— Feature on when resolution fails (use when the gate protects removal of legacy behavior).
TypeScript Support
Add a declaration file (e.g. src/types/scoped-modules.d.ts) so TypeScript recognizes the gate imports:
declare module '@salesforce/gate/*' {
export function isOpen(map: { fallback?: boolean }): boolean;
export function hasError(): boolean;
const gate: { isOpen: (map: { fallback?: boolean }) => boolean; hasError: () => boolean };
export default gate;
}
Custom Resolver
For container-specific resolution (e.g. Core Gater, metadata), provide a custom resolver:
import type { GateResolver } from '@lwrjs/gate-module-provider';
const myResolver: GateResolver = async (gateName, runtimeParams) => {
// Resolve from Core, metadata API, etc.
const enabled = await fetchGateState(gateName);
return { isOpen: enabled, hasError: false };
};
// In lwr.config.json moduleProviders:
// ["@lwrjs/gate-module-provider", { resolver: myResolver }]
The resolver receives (gateName: string, runtimeParams: RuntimeParams) and returns { isOpen: boolean, hasError?: boolean } (or a Promise of that).
Static Builds (MRT)
Gate modules are compiled into static bundles at build time. The gate provider and gates.json are not required in the SSR lambda runtime; resolution happens during the build.
gates.json Caching (Dev Mode)
The JSON resolver caches the parsed gates.json for the lifetime of the process. When editing gates.json during development, restart the dev server for changes to take effect. Resolution is effectively build-time for both dev and production.