Active npm 1.0.3 @soasap-com/react-sdk

SOASAP React SDK

Lightweight, production-ready feature flags SDK for React 18+ — lock-free O(1) reads, local-first evaluation with persistent browser cache, real-time SSE updates, reactive hooks, zero runtime dependencies, and graceful offline behavior. Vite, Webpack, Next.js client components, CRA.

Installation

Install from npm. Requires React 18+ as a peer dependency. Supports React 18 and 19+.

npm install @soasap-com/react-sdk react

Quick start

Wrap your app with SoasapProvider, enable preload: true for non-blocking startup sync, and read flags with hooks that re-render when values change.

import { SoasapProvider, useSoasapBool } from '@soasap-com/react-sdk';

function App() {
  return (
    <SoasapProvider
      options={{
        apiKey: import.meta.env.VITE_SOASAP_API_KEY!,
        preload: true,
      }}
    >
      <CheckoutPage />
    </SoasapProvider>
  );
}

function CheckoutPage() {
  const newCheckout = useSoasapBool('new-checkout');

  if (newCheckout) {
    return <NewCheckout />;
  }
  return <LegacyCheckout />;
}

Imperative client (no hooks)

Use createSoasapClient() for sync reads outside React components — event handlers, utilities, or non-UI code paths.

import { createSoasapClient } from '@soasap-com/react-sdk';

const flags = createSoasapClient({
  apiKey: import.meta.env.VITE_SOASAP_API_KEY!,
  preload: true,
});

if (flags.getBool('new-checkout')) {
  console.log('New checkout enabled');
}

Typed access

Reactive hooks for UI components and sync getters on the imperative client. JSON keys from the dashboard are returned as stored (typically camelCase).

// Hooks (reactive)
useSoasapBool('feature-x');
useSoasapNumber('rate-limit', 100);
useSoasapString('ui-theme', 'light');
useSoasapJson<CheckoutConfig>('checkout-config');

// Client (sync)
flags.getBool('feature-x');
flags.getNumber('rate-limit', 100);
flags.getString('ui-theme', 'light');
flags.getJson<CheckoutConfig>('checkout-config');

Startup sync

preload: true (recommended) starts the SSE worker in the background, loads the browser cache on cold start, and never blocks your app shell render. Without it, lazy mode defers the network connection until the first flag read.

In lazy mode, the first evaluation uses default values (or cached flags from localStorage if available) while SSE connects in the background.

// Immediate sync (recommended)
<SoasapProvider options={{ apiKey: '...', preload: true }} />

// Lazy sync — first read falls back to defaults or storage cache
<SoasapProvider options={{ apiKey: '...' }} />

Shared client instance

Create one client per browser tab and pass it to the provider. The provider only auto-closes clients it creates itself.

const flags = createSoasapClient({
  apiKey: import.meta.env.VITE_SOASAP_API_KEY!,
  preload: true,
});

export function Root() {
  return (
    <SoasapProvider client={flags}>
      <Router />
    </SoasapProvider>
  );
}

Vite integration

Mount the provider at your app root. Works with Vite, Webpack, CRA, and Next.js client components.

// main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { SoasapProvider } from '@soasap-com/react-sdk';
import { App } from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <SoasapProvider
      options={{
        apiKey: import.meta.env.VITE_SOASAP_API_KEY,
        preload: true,
      }}
    >
      <App />
    </SoasapProvider>
  </StrictMode>,
);

Error handling & observability

Hook background diagnostics without affecting the hot path. Sources: Network, Storage, Parser.

import { SoasapErrorSource, createSoasapClient } from '@soasap-com/react-sdk';

createSoasapClient({
  apiKey: '...',
  onError: (ctx) => {
    console.error(
      `[${ctx.source}] transient=${ctx.isTransient}`,
      ctx.exception.message);
  },
});

// SoasapErrorSource.Network | .Storage | .Parser

Production safety & guardrails

Getters and hooks never throw (except missing SoasapProvider for hooks).

Offline resiliency

ScenarioBehavior
API unavailableUses stale cached flags
SSE disconnectedKeeps last known snapshot
First visit without cacheReturns default values
Invalid payloadPayload ignored
Storage quota / failureIn-memory mode continues
Persistent network issuesAutomatic reconnect with backoff

Browser storage key: soasap:cache:<api-key-prefix>.
Override with cacheKeyPrefix: 'myapp:flags' or a custom storage implementation.

Architecture

[Hot Path]   getBool() / hooks → currentSnapshot ref → O(1) lookup
                              ↑
                              | (atomic reference swap + subscribe)
[Background] SSE → SseEventParser (5MB cap) → StorageWriteCoalescer → localStorage

Supported environments

← All SDKs