---
id: react/setup
title: Setup & Boot Modes
---

React adapter for `@secundus-studio/stift-core@0.4.0`. Core stays headless; this package is the `useSyncExternalStore`-backed surface (via `@tanstack/react-store`) on top of the same store, pack manager, and config the vanilla API reads — every hook is a thin wrapper over an existing headless method.

TanStack-style unified surface: `@secundus-studio/stift-react@0.4.0` re-exports `@secundus-studio/stift-core@0.4.0`, so a React app installs this one package and imports everything from it — `createStift`, the `Stift` type, `RegisteredLocale`, `PersisterAdapter`, hooks, providers, all of it.

```bash
pnpm add @secundus-studio/stift-react@0.4.0 @secundus-studio/stift-vite-plugin@0.4.0
```

## Provider

```tsx
import { createStift, StiftProvider, BootFallback, useTranslation } from '@secundus-studio/stift-react'
import { createStiftRuntime } from 'virtual:stift/runtime'

const Stift = createStift({ ...createStiftRuntime(), persister })
const locale = await Stift.detectLocale()

<StiftProvider Stift={Stift} boot={() => Stift.initialize({ locale })}>
  <BootFallback fallback={<Spinner />}>
    <App />
  </BootFallback>
</StiftProvider>

function Cart() {
  const { t, ready } = useTranslation('checkout')
  return <h1>{t('title')}</h1>
}
```

Instance wiring (detectors, `localePersistence`, persister): [Quickstart](./quickstart.md). Generated artifacts: [Build](../tooling/build.md).

## Boot modes

Three supported modes — pick one per app (or per subtree):

- **`fallback` (splash):** `boot` prop + `<BootFallback fallback={…}>`. Renders the fallback until boot settles, then children. The fallback must not require translation. A rejected `boot` still releases the gate and reports through `onBootError` (defaults to `console.error`).
- **`suspense` (per-component):** default. `useTranslation(ns)` suspends until its namespaces load — wrap in `<Suspense>`. Nothing suspends for cached or seeded namespaces.
- **`none` (immediate):** omit `boot`/`BootFallback`, pass `{ suspense: false }`, gate on `ready`. Renders instantly with raw-key fallback while loads stream in — suits hybrid client/server pages and client islands.

Boot imperatively in the client entry (as the examples do) by omitting `boot` and calling `initialize()` yourself.

## SSR hydration

No SSR boot mode — the server renders translated HTML and the client adopts it:

```tsx
// client
const Stift = createStift({ ...runtime, hydrate: window.__STIFT_STATE__ })
<StiftProvider Stift={Stift}> {/* no boot prop, no BootFallback */}
  <App /> {/* seeded namespaces never suspend */}
</StiftProvider>
```

The server renders `t()` results directly (`@secundus-studio/stift-core/server`: `resolveLocale` + `FsJsonSource`), then serializes `{ locale, namespaces }` into the HTML (e.g. `window.__STIFT_STATE__`). Set `<html lang>` from the server-resolved locale; the client never re-detects.

## Hooks by concern

The hooks are deliberately split by internationalization domain:

| Hook | Domain | Returns | Headless equivalent |
|---|---|---|---|
| `useStift()` | i18n (system root) | `Stift` instance | `createStift()` |
| `useTranslation(ns?)` | i18n (messages) | `{ t, ready }` (`t.rich` / `t.markdown` on `t`) | `Stift.scope` / `Stift.t` |
| `usePackStatus()` | i18n (resources) | `{ pendingLocale, isBooting, packInstalls, getStatus }` | `Stift.state` + `subscribe` |
| `useLocale()` | l10n (locale) | `{ locale, direction, setLocale, context }` | `Stift.getLocale/getDirection/setLocale` |
| `useFormatter()` | l10n (conventions) | formatter functions only | `@secundus-studio/stift-format@0.4.0` / `Intl` |
| `useConfig()` | g11n (system config) | `Readonly<StiftRuntimeConfig>` | `Stift.config` |

### The three-letter words

- **g11n (globalization)** is the umbrella — i18n + l10n together. In this adapter it's the `Stift` instance and `useConfig()`: system configuration and strategy (supported locales, currencies, numbering systems, params/context declarations, memory, loading strategy, detection, security tier).
- **i18n (internationalization)** is the enabling engineering: message keys, `t`, ICU, RTL support, detection, and the translation **resources** (packs). `useStift`, `useTranslation`, `usePackStatus`.
- **l10n (localization)** is the adaptation for one locale: the active locale, switching, and national conventions (date/number formatting). `useLocale`, `useFormatter`.

A "pack" is translation files — i18n resources, not a locale concern — so `usePackStatus` reports pack download/install state, not a locale state.

## Strict per-concern returns

Hooks never return overlapping meta:

- `useTranslation` returns only `{ t, ready }`. `ready` is `true` once every requested namespace is cached for the active locale (i18next-style flag); pair it with `{ suspense: false }` to gate instead of suspending.
- `useLocale` returns locale state only — the instance belongs to `useStift()`, config to `useConfig()`.
- `useFormatter` returns formatting functions only; locale/direction come from `useLocale()`.

## Runtime config

`Stift.config` (headless) and `useConfig()` (React) expose the effective runtime config — normalized from the generated `virtual:stift/runtime` `config` object plus any loose `createStift` overrides, shallow-frozen for the instance lifetime. `security.staticKey` never crosses the runtime boundary. Declaring it: [stift.config.ts](../configuration/config-file.md).

## Next

- Rendering messages, values, rich text, agreement context → [Translating & Values](./translating.md)
- Measurement units → [Units](./units.md)
- Offline packs and `usePackStatus` patterns → [Offline & Persistence](../runtime/offline-persistence.md)
