Browse docs

Getting Started

AI Setup Prompt

ReactVanilla

Copy everything below the line into an agent to set Stift up in a fresh repo. It distills the getting-started and config skills into one paste.


You are setting up Stift by Secundus, typed build-time-compiled i18n for TypeScript. Packages live under the @secundus-studio scope and are pre-release: install exactly version 0.4.0, never latest.

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

If the registry needs auth, add the @secundus-studio scope line to .npmrc and run with NPM_TOKEN set.

Wire the Vite plugin in vite.config.ts:

import { stiftPlugin } from '@secundus-studio/stift-vite-plugin'
export default defineConfig({
  plugins: [stiftPlugin()],
})

Write stift.config.ts:

import { defineConfig } from '@secundus-studio/stift-generator'
export default defineConfig({
  locales: { default: 'en', supported: ['en'] },
  namespaces: { dir: 'locales', discovery: 'convention', default: ['common'] },
  messageFormat: 'icu-mf1',
  detection: { order: ['explicit', 'persisted', 'navigator'], fallback: 'en' },
  loadingStrategy: 'critical-then-full-background',
})

Start with one locale and one namespace. Add params.context, units, and security.tier only when the feature calls for them. The default locale is the schema — it owns every namespace's key set, and new keys propagate to every other locale automatically.

Create locales/en/common.json with one key:

{ "greeting": "Hello, {name}!" }

Create the runtime in src/stift.ts — everything declarative arrives via the generated virtual:stift/runtime module; this file supplies only what can't be serialized:

import { createStiftApp } from '@secundus-studio/stift-react'
import { createStiftRuntime } from 'virtual:stift/runtime'
import { createBestEffortPersister } from '@secundus-studio/stift-persisters'

export const { stift, boot } = await createStiftApp({
  ...createStiftRuntime(),
  persister: createBestEffortPersister,
})

Boot before rendering in src/main.tsx:

import { stift, boot } from './stift'

await boot()
render(
  <StiftProvider stift={stift}>
    <Suspense fallback={<FullPageLoader />}><App /></Suspense>
  </StiftProvider>,
)

Use it in a component:

const { t } = useTranslation('common')
t('greeting', { name: user.name })

Verify: pnpm dev, the greeting renders from locales/en/common.json.

When you need more than this page, load the skill that owns the topic (pnpm exec intent load <package>#<skill>): stift-vite-plugin#build for plugin options, stift-core#core for boot and detection, stift-generator#config for the config schema, stift-generator#authoring-translations for writing keys, stift-react#react and stift-react#values for hooks and formatting. The full routing layer is at https://stift.secundus.studio/llms.txt. The canonical reference app is examples/tanstack-spa in the Stift repo.