Devtools
Stift ships as a plugin registered into the unified TanStack Devtools shell — the trigger button, dock, tabs, theme, and hotkeys are the shell's own; only the panel content is Stift's. Three tabs: Overview (status header, locale switch, namespace buckets, missing-key/param logs, pack state), Config (security, detection pipeline, loading strategy, locales — editable, saved to stift.config.ts), and Namespaces (buckets + cross-locale table with live editing).
Install and mount
pnpm add @secundus-studio/stift-tanstack-devtool-plugin@0.4.0
// devtools.tsx
import { useMemo } from 'react'
import { TanStackDevtools } from '@tanstack/react-devtools'
import { createStiftDevtoolsPlugin } from '@secundus-studio/stift-tanstack-devtool-plugin/tanstack'
import { useStift } from '@secundus-studio/stift-react'
export function Devtools() {
const Stift = useStift()
const plugins = useMemo(() => {
if (!import.meta.env.DEV) return [] // statically stripped in prod
const [StiftDevtoolsPlugin] = createStiftDevtoolsPlugin({
Stift,
supportedLocales: Stift.config.locales.supported,
allowFileWrites: true,
})
return [StiftDevtoolsPlugin()]
}, [Stift])
if (!import.meta.env.DEV) return null
return <TanStackDevtools config={{ position: 'bottom-left', panelLocation: 'bottom' }} plugins={plugins} />
}
The Stift instance is created asynchronously at boot, so build the plugin from the provider context (useStift()) rather than a module-scope singleton. import.meta.env.DEV lets Vite's dead-code elimination drop the whole shell from production.
createStiftDevtoolsPlugin returns the documented TanStack factory pair: Plugin() mounts the panel content in a shell tab; NoOpPlugin() renders nothing, for prod builds that still build the plugins array.
Panel actions
All via StiftDevtools, all surfacing errors through pack status + hooks.onError: locale switch, param/unit set+reset, pack install, wipe, stale refresh, single-ping update check (checkAllForUpdates), live-edit writes, and config writes (writeConfigValue(['detection', 'order'], …) → stift.config.ts, validated with rollback, applied on reload). Pack rows hide unless pack.durability is 'durable' (Offline & Persistence).
Dev-server bridge
The panel talks to the vite plugin's dev bridge (/__stift_devtools/table, /write) via the default createViteDevtoolsTransport. Writes only work when both stiftPlugin({ devtools: { allowFileWrites: true } }) and the factory's allowFileWrites: true are set — otherwise the server never mounts the write route. In-process and TanStack EventClient transports are also available for non-Vite setups.
Editing a string in the panel persists through the /write bridge and lands in place with no page refresh (verified in the Quickstart).
Next
- Pack lifecycle behind the panel → Offline & Persistence
- Missing-key/param logs → Messages & Keys