@skystate/react
@skystate/react provides a React context provider and hooks that integrate SkyState remote config into React applications. It is built on @skystate/core and uses useSyncExternalStore for efficient, concurrent-safe rendering.
Installation
bash
npm install @skystate/react@skystate/react depends on @skystate/core. You do not need to install @skystate/core separately.
React 18 or later is required.
Public API
Provider
| Export | Description |
|---|---|
SkyStateProvider | Context provider. Wrap your app (or a subtree) to make config available to hooks. |
SkyStateProviderProps | TypeScript type for the provider's props. |
Hooks
| Export | Description |
|---|---|
useProjectConfig(path, fallback?) | Read a config value and subscribe to changes. |
useProjectConfigStatus() | Read the connection status: lastFetched and error. |
Utilities
| Export | Description |
|---|---|
defaultReactResolver | The default environment resolver used by SkyStateProvider. Reads import.meta.env.MODE or process.env.NODE_ENV. |
Re-exported from @skystate/core
| Export | Description |
|---|---|
SkyStateError | Typed error class with code and optional status. |
ConfigEnvelope | Type: { version, lastModified, config } |
Version | Type: { major, minor, patch } |
Environment | Type: 'development' | 'staging' | 'production' |
Sections
- SkyStateProvider — props reference and setup patterns
- useProjectConfig — reading config values with full type safety
- useConfigStatus — connection status, error handling, and last-fetched time
Quick Example
tsx
// main.tsx — wrap your app in the provider
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { SkyStateProvider } from '@skystate/react';
import App from './App';
const apiUrl = import.meta.env.VITE_SKYSTATE_API_URL ?? 'https://api.skystate.io';
const accountSlug = import.meta.env.VITE_SKYSTATE_ACCOUNT_SLUG ?? 'my-account';
const projectSlug = import.meta.env.VITE_SKYSTATE_PROJECT_SLUG ?? 'my-project';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<SkyStateProvider
apiUrl={apiUrl}
accountSlug={accountSlug}
projectSlug={projectSlug}
resolveEnvironment={() => import.meta.env.MODE ?? 'development'}
>
<App />
</SkyStateProvider>
</StrictMode>,
);tsx
// FeatureFlag.tsx — read a config value in a component
import { useProjectConfig } from '@skystate/react';
export function NewCheckoutButton() {
const { data: enabled, isLoading } = useProjectConfig<boolean>(
'features.newCheckout',
false,
);
if (isLoading) return null;
if (!enabled) return <LegacyCheckoutButton />;
return <NewCheckoutButton />;
}tsx
// StatusBar.tsx — display connection status
import { useProjectConfigStatus } from '@skystate/react';
export function StatusBar() {
const { lastFetched, error } = useProjectConfigStatus();
if (error) return <div>Config error: {error.message}</div>;
if (!lastFetched) return <div>Connecting...</div>;
return <div>Config synced at {lastFetched.toLocaleTimeString()}</div>;
}How Rendering Works
@skystate/react uses React's useSyncExternalStore to subscribe to the ConfigStore. This means:
- Components re-render only when the specific config path they subscribed to changes.
- A component reading
'banner.text'does not re-render when'features.maxItems'changes. - Object references are stable across renders when values have not changed (structural sharing in the cache).
- The provider and hooks are safe to use in React Strict Mode and React Concurrent Mode.