Skip to content

SkyStateProvider

SkyStateProvider is a React context provider that initialises a ConfigStore, begins fetching config from the server, and makes it available to all descendant hooks.

Place it near the root of your component tree (or near the root of the subtree that needs remote config). All useProjectConfig and useProjectConfigStatus calls below it in the tree will read from the same store instance.


Props

typescript
interface SkyStateProviderProps {
  apiUrl: string;
  accountSlug: string;
  projectSlug: string;
  /** Custom resolver function. Falls back to defaultReactResolver if omitted. */
  resolveEnvironment?: () => string;
  children: ReactNode;
}

apiUrl

The base URL of the SkyState API, without a trailing slash.

tsx
<SkyStateProvider apiUrl="https://api.skystate.io" ...>

During local development when using the Docker Compose stack, use the proxy URL:

tsx
<SkyStateProvider apiUrl="http://localhost:8080/api" ...>

For Vite projects, read this from an environment variable so it can vary by environment:

tsx
const apiUrl = import.meta.env.VITE_SKYSTATE_API_URL ?? 'https://api.skystate.io';

accountSlug

Your account slug from the SkyState dashboard. This is a URL-safe identifier created when you sign up.

tsx
<SkyStateProvider accountSlug="acme-corp" ...>

projectSlug

The slug of the project whose config you want to read. Project slugs are defined when you create a project with skystate init or from the dashboard.

tsx
<SkyStateProvider projectSlug="website" ...>

resolveEnvironment

An optional function that returns the environment slug to connect to. Must return one of 'development', 'staging', or 'production'.

If omitted, SkyStateProvider uses defaultReactResolver, which reads import.meta.env.MODE (Vite) or process.env.NODE_ENV (Node.js/webpack).

tsx
// Explicit resolver (recommended for clarity)
<SkyStateProvider
  resolveEnvironment={() => import.meta.env.MODE ?? 'development'}
  ...
>

// Custom mapping
<SkyStateProvider
  resolveEnvironment={() => {
    const env = import.meta.env.VITE_APP_ENV;
    if (env === 'prod') return 'production';
    if (env === 'stg') return 'staging';
    return 'development';
  }}
  ...
>

If resolveEnvironment returns an invalid value or throws, the provider throws a SkyStateError synchronously during rendering. In development this will cause an error overlay. In production, it will propagate to the nearest error boundary.

children

Standard React children. All descendants can call useProjectConfig and useProjectConfigStatus.


devKey

The dev API key is not a prop. It is read automatically from the environment:

  • import.meta.env.SKYSTATE_DEV_KEY (Vite / esbuild / Rollup)
  • process.env.SKYSTATE_DEV_KEY (Node.js / webpack / CRA)

When the key is found and the resolved environment is 'development', auto-registration is enabled. See Auto-registration for details.


Usage Examples

Minimal setup (Vite)

tsx
import { SkyStateProvider } from '@skystate/react';

createRoot(document.getElementById('root')!).render(
  <SkyStateProvider
    apiUrl="https://api.skystate.io"
    accountSlug="my-account"
    projectSlug="my-project"
  >
    <App />
  </SkyStateProvider>,
);

With no resolveEnvironment prop, the provider calls defaultReactResolver, which reads import.meta.env.MODE. In a Vite dev server this returns 'development'. In a vite build production build it returns 'production'.

Explicit environment from environment variables (Vite)

tsx
// main.tsx
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>,
);

Environment file setup

Create these files in your project root:

# .env (committed — non-sensitive defaults)
VITE_SKYSTATE_API_URL=https://api.skystate.io
VITE_SKYSTATE_ACCOUNT_SLUG=my-account
VITE_SKYSTATE_PROJECT_SLUG=my-project

# .env.local (not committed — developer-specific)
SKYSTATE_DEV_KEY=devkey_abc123...

Store Lifecycle

The provider creates the store during the initial render, not inside a useEffect. This ensures the store is available on the first render pass and hooks can read from it immediately (returning isLoading: true if no data has arrived yet).

When the provider unmounts, a useEffect cleanup function calls store.dispose(), which cancels in-flight requests, clears subscriptions, and removes the store from the singleton registry.

If you remount the same provider (for example, during React Strict Mode's double-invocation), getOrCreateStore returns the same singleton, so the HTTP request is not duplicated.


Using Multiple Providers

Each SkyStateProvider instance connects to one (apiUrl, accountSlug, projectSlug, environment) tuple. If your application reads config from multiple projects, nest or render multiple providers:

tsx
// Reads from 'website' project
<SkyStateProvider accountSlug="acme" projectSlug="website" apiUrl="...">
  <WebsiteSection />
</SkyStateProvider>

// Reads from 'admin' project
<SkyStateProvider accountSlug="acme" projectSlug="admin" apiUrl="...">
  <AdminSection />
</SkyStateProvider>

Hooks will use the nearest SkyStateProvider ancestor in the tree.

Built with VitePress