Browse docs

Core

Setup & Boot

ReactVanilla

Vanilla TypeScript gets the full runtime: the same store, packs, types, and detection pipeline the React adapter wraps — with no React and no DOM requirement. Works in Node, browsers, workers, anywhere TypeScript runs.

pnpm add @secundus-studio/stift-core@0.4.0 @secundus-studio/stift-vite-plugin@0.4.0

There is no unified re-export here — @secundus-studio/stift-core@0.4.0 is the surface. React apps use @secundus-studio/stift-react@0.4.0 instead, which re-exports all of it.

Create the instance

import { createStift } from '@secundus-studio/stift-core'
import { createStiftRuntime } from 'virtual:stift/runtime' // emitted by the vite-plugin

const Stift = createStift({
  ...createStiftRuntime(), // locales, detection, state, strategy, source, catalog
  persister, // offline pack storage: OPFS / IndexedDB / Cache API (optional but recommended)
  hooks: { onError: (e) => console.error(e) }, // pack failures, missing keys
})

createStift is synchronous and side-effect-free. Persisted user state (the locale preference plus setParam/setUnit overrides) is configured in stift.config.ts (state: { prefix: '__stift_', storage: 'webStorage' }) and read synchronously at construction; persister is where downloaded pack bytes live. Without a persister, packs are memory-only (re-fetched each boot) — Stift.pack.durability reports 'durable' | 'memory' | 'none' so UI can gate install affordances honestly. Offline wiring: Offline & Persistence.

Boot (offline-first)

const locale = await Stift.detectLocale() // explicit ? persisted ? navigator ? fallback
await Stift.initialize({ locale })        // strategy decides what loads when

initialize fast-paths returning visitors: installed packs verify + activate from the persister with zero network, and the reactive packInstalls status is rehydrated from disk so it reads installed from the first notification. Strategy choices: Loading Strategies.

Reactivity without a framework

Stift.subscribe((state) => …) fires on any store mutation (locale, cache, packInstalls, missingKeys…); read Stift.state / Stift.getLocale() / Stift.getDirection() synchronously anytime. The store underneath is @tanstack/store — any reactive binding (including React via useSyncExternalStore) can subscribe to the same instance.

SSR hydration (no SSR boot mode)

The server renders t() results directly (@secundus-studio/stift-core/server: resolveLocale + FsJsonSource), then serializes what it rendered:

// server, after rendering
const state = serializeStiftState(Stift) // { locale, namespaces }
// ? inject into HTML as window.__STIFT_STATE__, set <html lang>
// client ? adopt it: no re-detection, no fallback flash, no re-fetch
const Stift = createStift({ ...runtime, hydrate: window.__STIFT_STATE__ })
await Stift.initialize({ locale: state.locale })

There is deliberately no 'ssr' boot strategy — hydration is the SSR story.

Next