ShipOS docs
@shipos/react SSR-safe

React

@shipos/react wraps @shipos/sdk in hooks. It's SSR-safe with no hydration mismatch, and components re-render live when flag config changes.

Install

bash
npm i @shipos/react

Quickstart

Wrap your tree in the provider, then read flags with useFlag:

tsx
import { ShipOSProvider, useFlag } from "@shipos/react";

function App() {
  return (
    <ShipOSProvider clientKey="sos_sdk_..." user={{ userId: "u_42" }}>
      <Checkout />
    </ShipOSProvider>
  );
}

function Checkout() {
  const newCheckout = useFlag("new-checkout", false);
  return newCheckout ? <CheckoutV2 /> : <CheckoutV1 />;
}

Hydration-safe by design

useFlag returns your defaultValue on the server and the first client render, then evaluates on mount. The server and client agree on that first paint, so there's no hydration mismatch.

Next.js App Router

The provider is client-only. Create a providers.tsx with "use client" and a public key:

app/providers.tsxtsx
"use client";

import { ShipOSProvider } from "@shipos/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ShipOSProvider
      clientKey={process.env.NEXT_PUBLIC_SHIPOS_SDK_KEY!}
      user={{ userId: "u_42" }}
    >
      {children}
    </ShipOSProvider>
  );
}

Use the hook in a client component:

app/checkout-button.tsxtsx
"use client";

import { useFlag } from "@shipos/react";

export function CheckoutButton() {
  const newCheckout = useFlag("new-checkout", false);
  return <button>{newCheckout ? "Checkout v2" : "Checkout"}</button>;
}

In server components, route handlers, and server actions, use @shipos/sdk directly:

app/page.tsxtsx
import { createClient } from "@shipos/sdk";

const shipos = createClient({ key: process.env.SHIPOS_SDK_KEY! });

export default async function Page() {
  const banner = await shipos.flag("promo-banner", { default: false });
  return <main>{banner && <PromoBanner />}</main>;
}

Seed the first render with bootstrap

Evaluate flags on the server and pass them to the provider's bootstrap prop. The first client render then shows real values instead of defaults, still fully hydration-safe.

API

HookReturnsDescription
useFlag(key, default, overrides?)TThe flag value. Returns default on the server and first client render, evaluates on mount, re-evaluates on config change. overrides { userId?, attributes? } replaces the provider user for this call.
useFlagDetail(key, default, overrides?){ value, reason, loading }Value plus reason; loading stays true until the first evaluation settles.
useShipOS()ShipOSClientThe underlying client, an escape hatch. Throws if called outside the provider.
createFlagStore(client, key, default, context?, bootstrap?)FlagStoreAdvanced / testing, a standalone store for a single flag.

Provider props

ShipOSProvider takes either a ready-made client, or clientKey plus optional baseUrl, defaults, and refreshInterval. Both forms also accept user and bootstrap.

Who owns the client

Given a clientKey, the provider creates and closes the client for you. Given a caller-supplied client, it never closes it, that's your responsibility.

Re-exports

createClient, ShipOSClient, and the wire types are re-exported from @shipos/sdk, so you can import everything from @shipos/react when you need both.