grill-me
Interview the user relentlessly about a plan or design until reaching shared understanding,…
Adding a new feature flag to the Webiny system. Use this skill when creating a new feature flag (simple boolean or nested group), gating a feature at the config/admin/API level, or wiring a flag into the WCP license system. Covers IFeatureFlagsDto, KnownFeatureFlag, Zod schema,
$ npx -y skills add webiny/webiny-js --skill add-feature-flag --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/add-feature-flagContext preview
The summary Claude sees to decide when to auto-load this skill.
Adding a new feature flag to the Webiny system. Use this skill when creating a new feature flag (simple boolean or nested group), gating a feature at the config/admin/API level, or wiring a flag into the WCP license system. Covers IFeatureFlagsDto, KnownFeatureFlag, Zod schema,
name: webiny-add-feature-flag description: > Adding a new feature flag to the Webiny system. Use this skill when creating a new feature flag (simple boolean or nested group), gating a feature at the config/admin/API level, or wiring a flag into the WCP license system. Covers IFeatureFlagsDto, KnownFeatureFlag, Zod schema, FeatureFlag.CanUse components, useFeatureFlags().isEnabled(), the API FeatureFlags abstraction, toDto(), and the LICENSE_CHECKS decorator pattern.
A WCP license is required for feature flags to work. The license is the gate; the config is the switch within the gate.
1. No license at all → false (everything off, config ignored) 2. License blocks the flag → false (config ignored) 3. License allows + config=false → false (config can disable what license allows) 4. License allows + config=true → true 5. License allows + config unset → true (license is the authority for unset flags) 6. Not in LICENSE_CHECKS + license exists + config unset → true 7. Not in LICENSE_CHECKS + license exists + config=false → false
Key points:
**File:** `packages/feature-flags/src/types.ts`
Add the new flag to `IFeatureFlagsDto`:
export interface IFeatureFlagsDto {
// ... existing flags
myNewFeature?: boolean;
}**File:** `packages/feature-flags/src/FeatureFlags.ts`
Add the string to the `KnownFeatureFlag` type:
export type KnownFeatureFlag = // ... existing flags "myNewFeature";
**File:** `packages/feature-flags/src/FeatureFlags.ts`
Add the flag to the `toDto()` method so the API returns it:
toDto() {
return {
// ... existing flags
myNewFeature: this.isEnabled("myNewFeature")
};
}**File:** `packages/project/src/extensions/FeatureFlags.tsx`
Add to the `paramsSchema` so users get validation in `webiny.config.tsx`:
myNewFeature: z.boolean().optional();
At the **config level** (controls whether extensions mount at build time):
// In the extension component (e.g., MyFeature.tsx)
import { FeatureFlag } from "@webiny/project";
export const MyFeature = () => (
<FeatureFlag.CanUse name="myNewFeature">
<Api.Extension src={...} />
<Admin.Extension src={...} />
</FeatureFlag.CanUse>
);Or add a named convenience component in `packages/project/src/components/FeatureFlag.tsx`:
function CanUseMyNewFeature({ children }: { children: React.ReactNode }) {
return <CanUse name="myNewFeature">{children}</CanUse>;
}At the **admin runtime level** (controls UI visibility):
import { useFeatureFlags } from "@webiny/app-admin";
const featureFlags = useFeatureFlags();
if (!featureFlags.isEnabled("myNewFeature")) {
return null;
}At the **API runtime level** (controls backend behavior):
import { FeatureFlags } from "~/features/featureFlags/abstractions.js";
// In a DI-resolved class:
constructor(private featureFlags: FeatureFlags.Interface) {}
someMethod() {
if (!this.featureFlags.get().isEnabled("myNewFeature")) {
return;
}
}Users configure flags in `webiny.config.tsx`:
export const FeatureFlags = () => (
<Project.FeatureFlags
features={{
myNewFeature: false // disabled
}}
/>
);Omitting a flag means the license decides (enabled if licensed, disabled if not). Setting a flag to `false` disables it even if the license allows it.
For flags with sub-options (like `aiPowerups` or `advancedAccessControlLayer`):
export interface IMyFeatureOptions {
subFeatureA?: boolean;
subFeatureB?: boolean;
}
export interface IFeatureFlagsDto {
myFeature?: boolean | IMyFeatureOptions;
}export type KnownFeatureFlag = "myFeature" | "myFeature.subFeatureA" | "myFeature.subFeatureB";
myFeature: this.isEnabled("myFeature")
? {
subFeatureA: this.isEnabled("myFeature.subFeatureA"),
subFeatureB: this.isEnabled("myFeature.subFeatureB")
}
: false;myFeature: z.union([
z.boolean(),
z.object({
subFeatureA: z.boolean().optional(),
subFeatureB: z.boolean().optional()
})
]).optional();// Disable entirely
<Project.FeatureFlags features={{ myFeature: false }} />
// Disable specific sub-feature
<Project.FeatureFlags features={{ myFeature: { subFeatureA: false } }} />A WCP license is required for any feature flag to work. Without a license,
Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.
Repo: webiny/webiny-js
Interview the user relentlessly about a plan or design until reaching shared understanding,…
Turn a PRD into a multi-phase implementation plan using tracer-bullet vertical slices, saved…
Webiny-only. Run all checks required before packages are ready for publish: deps, build,…
Use when running tests. Shows how to run tests for a single package, including OpenSearch…
Generate, refresh, and maintain Webiny MCP server skills from source documentation and…
Create a PRD through user interview, codebase exploration, and module design, then submit as…