React
A hooks and provider wrapper around the JavaScript/TypeScript SDK, built
for React apps. Ships as ESM and CJS with full TypeScript types, and is
safe to import from Next.js Server Components (marked 'use client').
shipsilently/react Install
npm install @shipsilently/reactbun add @shipsilently/reactyarn add @shipsilently/reactpnpm add @shipsilently/reactWrap your app in the provider
ShipSilentlyProvider creates one client for the component tree,
evaluates every flag for the given context, and subscribes to
real-time updates, falling back to polling.
// Mount the provider once, above anything that reads a flag. It opens a single
// stream for the given context and re-renders consumers when flags change.
//
// The provider compares the context's *contents*, not its identity — you do not
// need to memoize the object you pass.
import { ShipSilentlyProvider } from '@shipsilently/react';
export function App({ user }: { user: { id: string; plan: string } }) {
return (
<ShipSilentlyProvider
config={{
apiKey: process.env.NEXT_PUBLIC_SHIPSILENTLY_KEY!,
}}
context={{ userId: user.id, plan: user.plan }}
>
<Checkout />
</ShipSilentlyProvider>
);
}
function Checkout() {
return <div>checkout</div>;
} context. The provider
re-subscribes when the object's contents change, not its identity,
so an inline object literal is fine and will not cause a resubscribe loop.
Props
| Prop | Type | Notes |
|---|---|---|
config | ShipSilentlyConfig | Same options as the JS SDK. |
context | UserContext | Attributes to evaluate against. Compared by contents. |
pollingIntervalMs | number | Optional. Passed through to the stream's polling fallback. |
children | ReactNode | — |
Hooks
All three must be called inside <ShipSilentlyProvider>;
they throw a descriptive error otherwise.
// Three hooks, all of which must be used inside <ShipSilentlyProvider>.
import { useFlag, useFlags, useShipSilentlyClient } from '@shipsilently/react';
// `useFlag` infers its return type from the default — `dark` is boolean,
// `variant` is string. No generic parameters to write.
export function Hero() {
const dark = useFlag('dark-mode', false);
const variant = useFlag('hero-variant', 'control');
return <section className={dark ? 'dark' : 'light'}>{variant}</section>;
}
// `useFlags` gives the whole map plus a first-load indicator. Use `loading` to
// avoid flashing the default value before the first evaluation lands.
export function DebugPanel() {
const { flags, loading } = useFlags();
if (loading) return <p>loading flags…</p>;
return (
<ul>
{Object.values(flags).map((f) => (
<li key={f.flagKey}>
{f.flagKey}: {String(f.value)} ({f.reason})
</li>
))}
</ul>
);
}
// Escape hatch to the underlying client — for `evaluate()` against a one-off
// context, or an explicit analytics flush.
export function FlushButton() {
const client = useShipSilentlyClient();
return <button onClick={() => void client.flushAnalytics()}>Flush</button>;
} | Hook | Returns | Notes |
|---|---|---|
useFlag(key, default) | T | Return type inferred from the default. Re-renders on updates. |
useFlags() | { flags, loading } | loading is true until the first evaluation lands. |
useShipSilentlyClient() | ShipSilentlyClient | Escape hatch to the raw client. |
loading to avoid a flash of the default.
Before the first evaluation resolves, useFlag returns the
default you passed. For anything visually disruptive, gate on
useFlags().loading and render a skeleton.
Lifecycle
The provider creates exactly one client and closes it on unmount, releasing
the stream and any analytics timers. Changing config after mount
does not rebuild the client, so read the key and URL from
environment variables rather than from state.
Next.js
The package ships with a 'use client' banner, so the provider
and hooks are safe to import from Client Components in the App Router.
For Server Components, route handlers, and middleware, don't reach for these hooks, evaluate with the JavaScript/TypeScript SDK directly. A common split is to evaluate server-side for the initial render and mount the provider only for the parts of the tree that need live updates.