Browse docs

Tooling

Devtools

ReactVanilla

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