npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@basestack/flags-react

v1.0.5

Published

React bindings for the Basestack Feature Flags SDK

Downloads

23

Readme

Basestack Feature Flags React Integration

React bindings for the Basestack Flags JS SDK. This package exposes a provider, hooks, hydration helpers, and SSR utilities that work across Vite, Next.js (App or Pages Router), and TanStack Start.

Features

  • Zero-config provider powered by the official @basestack/flags-js client.
  • Hooks for component-level reads (useFlag, useFlags, useFlagsClient).
  • Server utilities to preload flags in frameworks with data loaders or RSC.
  • Hydration helpers for streaming initial flag snapshots safely to the client.
  • Tree-shakeable ESM output built with tsdown and linted/formatted via Biome.

Installation

bun install @basestack/flags-react @basestack/flags-js
npm install @basestack/flags-react @basestack/flags-js
yarn add @basestack/flags-react @basestack/flags-js

React 18+ is required and should already exist in your project. The package ships as pure ESM and targets modern browsers/runtime APIs.

Local development

The repository uses Bun as the package manager and script runner:

bun install           # install dependencies
bun run lint          # biome lint (restricted to src + config files)
bun run test          # vitest suite
bun run build         # compile to dist/ via tsdown

All examples rely on the compiled dist/ output, so run bun run build before opening any of them.

Quick start (React + Vite)

import { FlagsProvider, useFlag } from "@basestack/flags-react/client";

const config = {
  projectKey: process.env.VITE_BASESTACK_PROJECT_KEY!,
  environmentKey: process.env.VITE_BASESTACK_ENVIRONMENT_KEY!,
};

function App() {
  return (
    <FlagsProvider config={config}>
      <HomePage />
    </FlagsProvider>
  );
}

function HomePage() {
  const { enabled, payload, isLoading } = useFlag<{ variant: string }>(
    "header"
  );

  if (isLoading) return <p>Loading…</p>;
  return enabled ? (
    <NewHomepage variant={payload?.variant} />
  ) : (
    <LegacyHomepage />
  );
}
  • The provider accepts the exact SDKConfig used by @basestack/flags-js plus optional props:
    • initialFlags: preload data, usually from SSR.
    • preload (default true): automatically fetch missing flags when initialFlags is empty.
    • onError: observe network/caching errors.
  • Hooks keep a shared cache, so subsequent components reuse already fetched flags.
  • Call refresh() from either useFlag or useFlags to re-query the API.

Import paths

Use the subpath that matches your runtime to avoid loading client-only hooks on the server:

  • @basestack/flags-react/clientFlagsProvider, hooks, readHydratedFlags, and SDK types. The file itself includes the "use client" directive.
  • @basestack/flags-react/serverfetchFlag, fetchFlags, createServerFlagsClient, FlagsHydrationScript, and shared constants.
  • @basestack/flags-react — server-friendly exports (no hooks or provider). Prefer the explicit /client and /server paths for new integrations.

Next.js (App Router)

// app/flags-config.ts
export const flagsConfig = {
  baseURL: process.env.BASESTACK_BASE_URL!,
  projectKey: process.env.BASESTACK_PROJECT_KEY!,
  environmentKey: process.env.BASESTACK_ENVIRONMENT_KEY!,
};
// app/layout.tsx
import {
  FlagsHydrationScript,
  fetchFlags,
} from "@basestack/flags-react/server";
import { Providers } from "./providers";
import { flagsConfig } from "./flags-config";

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const flags = await fetchFlags(flagsConfig);

  return (
    <html lang="en">
      <body>
        <Providers initialFlags={flags}>{children}</Providers>
        <FlagsHydrationScript flags={flags} />
      </body>
    </html>
  );
}
// app/providers.tsx
"use client";

import { FlagsProvider } from "@basestack/flags-react/client";
import type { Flag } from "@basestack/flags-js";
import type { ReactNode } from "react";
import { flagsConfig } from "./flags-config";

export function Providers({
  children,
  initialFlags,
}: {
  children: ReactNode;
  initialFlags?: Flag[];
}) {
  return (
    <FlagsProvider
      config={flagsConfig}
      initialFlags={initialFlags}
      preload={!initialFlags?.length}
    >
      {children}
    </FlagsProvider>
  );
}

Use fetchFlag() inside Server Components or Route Handlers if you only need a single slug.

Route Handler + Server Functions demo

The App Router example also includes:

  • GET /api/flags (app/api/flags/route.ts) to prove the SDK works inside a Route Handler / API route.
  • A /server-functions page that lists current flag states on the server and ships a ServerActionDemo client component which invokes a server action powered by fetchFlag.

Next.js (Pages Router)

// pages/_app.tsx
import type { AppProps } from "next/app";
import { FlagsProvider } from "@basestack/flags-react/client";

const config = {
  projectKey: process.env.NEXT_PUBLIC_BASESTACK_PROJECT_KEY!,
  environmentKey: process.env.NEXT_PUBLIC_BASESTACK_ENVIRONMENT_KEY!,
};

export default function MyApp({
  Component,
  pageProps,
}: AppProps<{ flags?: Flag[] }>) {
  const initialFlags = pageProps.flags ?? [];

  return (
    <FlagsProvider
      config={config}
      initialFlags={initialFlags}
      preload={!initialFlags.length}
    >
      <Component {...pageProps} />
    </FlagsProvider>
  );
}
// pages/index.tsx
import { fetchFlags } from "@basestack/flags-react/server";
import { useFlag } from "@basestack/flags-react/client";
import type { GetServerSideProps } from "next";
import type { Flag } from "@basestack/flags-js";

export const getServerSideProps: GetServerSideProps<{ flags: Flag[] }> = async () => {
  const flags = await fetchFlags({
    baseURL: process.env.BASESTACK_BASE_URL!,
    projectKey: process.env.BASESTACK_PROJECT_KEY!,
    environmentKey: process.env.BASESTACK_ENVIRONMENT_KEY!,
  });

  return {
    props: { flags },
  };
};

### API Route

Add a legacy API route that relies on the same server helper:

```ts
// pages/api/flags.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { fetchFlags } from "@basestack/flags-react/server";
import { flagsConfig } from "../../flags-config";

export default async function handler(_req: NextApiRequest, res: NextApiResponse) {
  try {
    const flags = await fetchFlags(flagsConfig);
    res.status(200).json({ flags });
  } catch (error) {
    res.status(500).json({ message: "Unable to load flags" });
  }
}

## TanStack Start

```tsx
// app/config/flags.ts
export const flagsConfig = {
  projectKey: process.env.BASESTACK_PROJECT_KEY!,
  environmentKey: process.env.BASESTACK_ENVIRONMENT_KEY!,
};
// routes/_app.tsx
import { createFileRoute, Outlet } from "@tanstack/react-router";
import { FlagsProvider } from "@basestack/flags-react/client";
import { fetchFlags } from "@basestack/flags-react/server";
import { flagsConfig } from "../config/flags";

export const Route = createFileRoute("/_app")({
  loader: async () => ({ flags: await fetchFlags(flagsConfig) }),
  component: () => {
    const { flags } = Route.useLoaderData();
    return (
      <FlagsProvider config={flagsConfig} initialFlags={flags} preload={false}>
        <Outlet />
      </FlagsProvider>
    );
  },
});

React + Vite (with server prefetch)

When running a Vite app locally you can hydrate the provider with data fetched from your backend (or from the included Node dev server):

// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { FlagsProvider } from "@basestack/flags-react/client";
import { fetchFlags } from "@basestack/flags-react/server";
import { App } from "./App";
import { flagsConfig } from "./flagsConfig";

async function bootstrap() {
  const container = document.getElementById("root");
  if (!container) throw new Error("Missing #root");

  let initialFlags = [];
  try {
    initialFlags = await fetchFlags(flagsConfig);
  } catch (error) {
    console.warn("Failed to preload flags", error);
  }

  createRoot(container).render(
    <StrictMode>
      <FlagsProvider
        config={flagsConfig}
        initialFlags={initialFlags}
        preload={initialFlags.length === 0}
      >
        <App />
      </FlagsProvider>
    </StrictMode>
  );
}

bootstrap();
// src/App.tsx
import { useFlag } from "@basestack/flags-react/client";

export function App() {
  const { enabled, payload, isLoading } = useFlag<{ variant?: string }>(
    "header"
  );

  if (isLoading) return <p>Checking...</p>;
  return enabled ? (
    <NewHomepage variant={payload?.variant} />
  ) : (
    <LegacyHomepage />
  );
}

Hooks reference

Import these from @basestack/flags-react/client.

  • useFlag(slug, options)
    • Returns { flag, enabled, payload, isLoading, error, refresh }.
    • Automatically fetches the flag once per mount (unless options.fetch === false).
    • options.defaultEnabled and options.defaultPayload let you provide fallbacks while loading.
  • useFlags()
    • Returns { flags, flagsBySlug, isLoading, error, refresh }.
    • Ideal for Admin/Settings UIs or debugging views.
  • useFlagsClient()
    • Provides direct access to the underlying FlagsSDK instance for advanced operations.

Server utilities

All server helpers live in the /server subpath:

import {
  fetchFlags,
  fetchFlag,
  createServerFlagsClient,
} from "@basestack/flags-react/server";
  • fetchFlags(config, slugs?): returns a Flag[]. When slugs is omitted, it loads the full project.
  • fetchFlag(slug, config): fetch exactly one flag.
  • createServerFlagsClient(config): returns a configured FlagsSDK so you can call low-level methods inside loaders.

Hydration helpers

import { FlagsHydrationScript } from "@basestack/flags-react/server";
import { readHydratedFlags } from "@basestack/flags-react/client";

// Server: embed the payload after the provider so client components can read it
<FlagsHydrationScript flags={flags} globalKey="__BASESTACK_FLAGS__" />;

// Client: read during bootstrapping (before rendering) if you need to avoid prop-drilling
const hydrated = readHydratedFlags();

FlagsHydrationScript encodes the snapshot using globalThis["__BASESTACK_FLAGS__"]. Pass globalKey to customize the name or set a CSP nonce when needed. readHydratedFlags only works in the browser, so import it from /client.

Scripts

| Command | Description | | ---------------- | -------------------------------------------- | | bun run build | Bundle ESM + type declarations with tsdown | | bun run dev | Watch-mode build for local development | | bun run lint | Run Biome lint rules | | bun run format | Format the entire repo with Biome | | bun run test | Execute the Vitest suite in JSDOM |

Use bun run prepublishOnly locally before releasing to ensure lint + tests stay green.

Development notes

  • Source lives in src/ and is compiled to dist/ via tsdown (ESM only).
  • The package exposes only modern ESM/Node 20+ syntax; no CommonJS output is produced.
  • Biome powers linting/formatting, so please keep editor integrations enabled.

Examples

Minimal framework demos live in examples/. Each project links @basestack/flags-react and the /client + /server subpaths to dist/, so you can test the SDK locally without publishing.

| Example | Highlights | Path | Dev command | | ----------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------- | ------------- | | Next.js 16 App Router | Provider wrapper, Route Handler (GET /api/flags), /server-functions page with Server Action demo | examples/next-app-router | bun run dev | | Next.js 16 Pages Router | _app wiring, getServerSideProps, pages/api/flags.ts API route | examples/next-pages-router | bun run dev | | React + Vite | Client-only bootstrap that preloads flags before render | examples/react-vite | bun run dev |

To run an example:

  1. bun run build at the repo root (ensures dist/ exists).
  2. cd examples/<example> and bun install.
  3. Provide BASESTACK_* environment variables (or use the demo IDs committed in each config).
  4. bun run dev to start the framework’s dev server.

See examples/README.md for more context.