Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Flags

Application feature flags

Bijection source
git clone https://github.com/bijectionhq/flags.git
cd flags
git checkout 83c58d7a4ff3f5ce5f6af806141b19cc056eaf8d

Bijection repository access is required. Request access.

Description

Application feature flags

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Available to evaluate

Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/flags. Based on vllnt/convex-flags. License: MIT. Consult the repository for license terms, notices, and contributor attribution.

From the upstream publisher

Feature flags as a Convex component — variants, targeting, percentage rollouts, and per-subject overrides, evaluated on the backend and streamed live to clients.

Explore the demo ↗

Upstream overview

From the upstream package documentation.

@vllnt/convex-flags separates a feature’s release decision from the code that implements it. Store boolean flags or variants and evaluate them against context supplied by your backend.

Define flags, targeting rules, weighted rollouts, and per-subject overrides. Evaluate flags in Convex queries and expose reactive results to clients. update changes selected fields; archive and restore support reversible retirement. Optional React hooks wrap host query references.

Your application ships the feature code, authenticates context, and gates management actions. A flag is not an access-control or payment-entitlement system. Percentage release control does not calculate experiment outcomes; measure those separately.

Maintained by bntvllnt at VLLNT. Find the author on GitHub and X.

Use cases

From the upstream package documentation.

  • Mobile apps: expose already-shipped functionality to a controlled audience.
  • SaaS releases: start with a small percentage of workspaces and expand gradually.
  • Games: enable events or select host-defined configurations.
  • AI tools: switch an existing model or prompt configuration, or disable a feature during an incident.

How it works

From the upstream package documentation.

Define flags, targeting rules, weighted rollouts, and per-subject overrides. Evaluate flags in Convex queries and expose reactive results to clients. update changes selected fields; archive and restore support reversible retirement. Optional React hooks wrap host query references.

Your application ships the feature code, authenticates context, and gates management actions. A flag is not an access-control or payment-entitlement system. Percentage release control does not calculate experiment outcomes; measure those separately.

Upstream package FAQ

Does @vllnt/convex-flags require a third-party feature flag service like LaunchDarkly or Statsig?

@vllnt/convex-flags is self-contained and runs entirely inside your Convex deployment. All flag definitions, targeting rules, rollout configuration, and per-subject overrides are stored in the component's own sandboxed tables. No external service, API key, or webhook is involved.

How does @vllnt/convex-flags handle authentication and authorization for flag management?

@vllnt/convex-flags is auth-agnostic. All management methods such as define, enable, disable, archive, and setOverride are internal to the component. You wrap them in your own Convex mutations and apply your authorization logic there before delegating to the component, so the component never makes auth decisions on your behalf.

Can @vllnt/convex-flags be used from React components?

Yes. The package ships an optional ./react entry point exporting useFlag and useFlags hooks. Both hooks accept the evaluate query you export from your convex/flags.ts file and return a FlagEvaluation object. React is an optional peer dependency, so backend-only consumers do not pull in React.

What evaluation order does @vllnt/convex-flags use when resolving a flag value?

@vllnt/convex-flags resolves flags in this order: per-subject override, archived state, ordered targeting rules, fallthrough rollout, then the flag's base value. If the flag key does not exist, the caller's provided default or false is returned.

Does @vllnt/convex-flags work on self-hosted Convex deployments?

Yes. @vllnt/convex-flags runs unchanged on both Convex Cloud and self-hosted convex-backend deployments. The only peer dependency is convex at version 1.36.1 or later.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Flags with Bijection.

Read: https://bijection.com/components/flags/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from vllnt/convex-flags, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

@vllnt/convex-flags

Feature flags as a Convex component — backend-evaluated and streamed to every connected client in real time: boolean kill-switches, multivariate variants, percentage rollouts, attribute targeting, and per-subject overrides, with no redeploy and no third-party flag SaaS.

const flags = new Flags(components.flags);
await flags.define(ctx, { key: "new-checkout", value: false });
await flags.enable(ctx, "new-checkout");
const on = await flags.isEnabled(ctx, "new-checkout", { subjectRef: userId }); // reactive in a query

Features

  • Boolean, multivariate & rollouts — boolean kill-switches, string/number variants, and weighted percentage rollouts with stable per-subject bucketing.
  • Attribute targeting — ordered rules (eq, in, contains, gt, …) over a host-supplied typed context.
  • Per-subject overrides — force any flag's value for one subject.
  • Lifecycle — archive (reversible) distinct from permanent delete.
  • Real-time — evaluated in Convex queries, so changes stream to clients over the same reactive subscriptions as your app data.
  • Data isolation — definitions and overrides live in the component's own sandboxed tables; no blast radius into your schema.
  • Reactive React hooks — optional ./react entry (useFlag/useFlags); backend-only consumers pull in zero React.
  • Auth-agnostic & dual-target — the host gates every management call; runs unchanged on Convex Cloud and self-hosted convex-backend.

Installation

npm install @vllnt/convex-flags

Register the component in your app's convex/convex.config.ts:

import { defineApp } from "convex/server";
import flags from "@vllnt/convex-flags/convex.config";

const app = defineApp();
app.use(flags);
export default app;

Peer dependency: convex@^1.36.1. react is an optional peer dependency, needed only for the ./react entry.

Usage

// convex/flags.ts — instantiate once, then re-export the public function refs the
// rest of your app (and the React hooks) call. The host owns auth: gate the
// management mutations here before delegating to the component.
import { v } from "convex/values";
import { Flags } from "@vllnt/convex-flags";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";

export const flags = new Flags(components.flags);

// Evaluation queries — reactive; `api.flags.evaluate` is what the React hooks below use.
export const evaluate = query({
  args: { key: v.string(), context: v.optional(v.any()), default: v.optional(v.any()) },
  handler: (ctx, args) =>
    flags.evaluate(ctx, args.key, { context: args.context, default: args.default }),
});

export const all = query({
  args: { context: v.optional(v.any()) },
  handler: (ctx, args) => flags.all(ctx, args.context),
});

// Management mutations — apply your own auth check before delegating.
export const define = mutation({
  args: { key: v.string(), value: v.any() /* + description/variants/rules/rollout */ },
  handler: (ctx, args) => {
    // assertAdmin(ctx);
    return flags.define(ctx, args);
  },
});

// Wrap the rest the same way — `update`, `enable`, `disable`, `archive`,
// `restore`, `setOverride`, `clearOverride` — each gated then delegated.
// Flags are data — create them from your own authorized mutations (host owns auth),
// then evaluate inside any query, mutation, or action.
import { query } from "./_generated/server";
import { flags } from "./flags";

// A targeted rollout: pro users get the new checkout; everyone else is a 50/50 split.
export const setup = async (ctx) => {
  await flags.define(ctx, {
    key: "new-checkout",
    value: false,
    rules: [{ conditions: [{ attribute: "plan", op: "eq", values: ["pro"] }], value: true }],
    rollout: { splits: [{ value: true, weight: 50 }, { value: false, weight: 50 }] },
  });
};

export const checkout = query({
  args: {},
  handler: async (ctx) =>
    (await flags.isEnabled(ctx, "new-checkout", { subjectRef: "user-123", attributes: { plan: "free" } }))
      ? loadNewCheckout(ctx)
      : loadLegacyCheckout(ctx),
});

API Reference

MethodKindResult
isEnabled(ctx, key, context?)queryboolean (value === true)
evaluate(ctx, key, options?)queryFlagEvaluation (value + reason)
variant(ctx, key, options?)querythe resolved value
get(ctx, key) / list(ctx)queryFlagDoc | null / FlagDoc[]
all(ctx, context?)queryRecord<string, FlagEvaluation> (reactive bootstrap)
define(ctx, definition)mutationCreate or fully replace a flag (value, variants, rules, rollout)
update(ctx, key, patch)mutationPartially update a flag — only supplied fields change, the rest are kept
enable(ctx, key) / disable(ctx, key)mutationToggle a boolean flag (sets value true/false)
archive(ctx, key) / restore(ctx, key)mutationReversibly retire / re-activate a flag
setOverride(ctx, key, subjectRef, value) / clearOverride(ctx, key, subjectRef)mutationForce / clear a per-subject value
remove(ctx, key)mutationHard-delete a flag and its overrides

evaluate resolves in order: override → archived → targeting rules → fallthrough rollout → flag value; an unknown key serves the caller default or false. enable/disable are boolean convenience (they set the value to true/false) — use define/update for multivariate flags. Full reference, types, operators, and the React hooks: docs/API.md.

React

Optional reactive hooks via the ./react entry (react is an optional peer dependency). Pass the evaluate query you exported from convex/flags.ts above — api.flags.evaluate:

import { useFlag } from "@vllnt/convex-flags/react";
import { api } from "../convex/_generated/api";

// api.flags.evaluate is the query exported in convex/flags.ts (Usage, above).
const checkout = useFlag(api.flags.evaluate, "new-checkout", { context: { subjectRef: userId } });
if (checkout?.value === true) { /* render the new checkout */ }
HookReturns
useFlag(query, key, options?)FlagEvaluation | undefined
useFlags(query, context?)Record<string, FlagEvaluation> | undefined

Security

  • Auth-agnostic — all management mutations are internal; wrap them in your own authorized mutations and apply auth there.
  • Flag keys are opaque host-chosen strings; the component never interprets or validates their structure.
  • Tables are sandboxed — the component never reads host or sibling tables.

See docs/API.md.

Testing

pnpm test           # single run
pnpm test:coverage  # enforced 100% on covered files

Tests run against the real component runtime via convex-test (@edge-runtime/vm), not mocks.

Contributing

See CONTRIBUTING.md.

Author

Built by bntvllnt · bntvllnt.com · X @bntvllnt

Part of the @vllnt Convex component fleet — vllnt.com

If this is useful, sponsor the work.

License

MIT

Keywords