Minimal, type-safe dev widget for React apps.
bun add @remcostoeten/dev-widgetimport { DevWidget } from '@remcostoeten/dev-widget/react'
export function App(): React.ReactElement {
return (
<>
<main>App</main>
<DevWidget
mode='development-only'
theme={{ mode: 'system', accent: '#3bb273' }}
features={{
performance: { enabled: true, fpsThreshold: 50 },
auth: { enabled: true, provider: 'better-auth' }
}}
/>
</>
)
}- Auth domain
- Routes domain
- Links domain
- Performance domain
- System domain
- Monitor domain
- React widget shell with tab categories
- Domain lifecycle (setup/start/stop) via runtime registry
- Plugin API with typed event subscription
- Event bus for cross-domain communication
- Next.js log ingestion route handler
- In-memory storage adapter
- Tauri storage adapter export
- Route discovery adapter for Next.js and React projects
- Right rail with diagnostics render panel
- Keyboard shortcuts (toggle + Escape)
- Accessibility: focus-visible, reduced-motion handling, live region announcements
- Internal subpath export (
@remcostoeten/dev-widget/internal) - Source maps and declaration output in package build
- CI publish artifact validation step
- Demo app build pipeline
- AI providers:
openaiandlocalmodes are declared but not implemented yet; onlyrulesandcustomare active (src/domains/performance/aiProviderFactory.ts). - Runtime prop updates:
DevWidgetmemoizes runtime byshouldRenderonly, so runtime does not re-bootstrap when non-visibility props change after mount (src/devWidget.tsx). - Storage wiring:
providers.storageis part of public config but not wired into runtime/domain persistence flow yet (src/types/public.tsandsrc/core/bootstrap.ts). - Endpoint validation: Next log handler accepts event payloads without schema validation; malformed event shapes can be persisted (
src/adapters/next/routeHandler.ts). - CI parity: local checks pass, but final release confidence still depends on a pushed branch and green remote GitHub Actions run.
import { createRuntime } from '@remcostoeten/dev-widget'
const runtime = createRuntime({ mode: 'always' })
await runtime.registerPlugin({
id: 'my-plugin',
version: '1.0.0',
register(ctx) {
ctx.onEvent('performance/drop-detected', function onDrop(payload) {
console.log('FPS drop:', payload.fps)
})
}
})import { DevWidget } from '@remcostoeten/dev-widget/react'
import { createLogHandler } from '@remcostoeten/dev-widget/next'
import { createTauriStorage } from '@remcostoeten/dev-widget/tauri'
import type { WidgetDomain } from '@remcostoeten/dev-widget/internal'The widget includes keyboard shortcuts, focus-visible styling, live announcements, and reduced-motion support.
See specs/10-dev-widget-accessibility-keyboard-spec.md for the detailed behavior contract.
enabled?: booleanmode?: 'development-only' | 'always'theme?: { mode?: 'system' | 'light' | 'dark'; accent?: string; density?: 'compact' | 'comfortable'; radius?: 'none' | 'sm' | 'md' | 'lg' }features?: { auth?: boolean | AuthOptions; routes?: boolean | RoutesOptions; performance?: boolean | PerfOptions; system?: boolean | SystemOptions; links?: boolean | LinksOptions }disable?: FeatureId[]providers?: { auth?: AuthConfig; ai?: AiConfig; storage?: StorageConfig }keyboard?: { toggleKey?: string; enableShortcuts?: boolean }accessibility?: { reducedMotion?: 'system' | 'always' | 'never'; announceUpdates?: boolean }rightRail?: { enabled?: boolean; defaultPanel?: string | null; collapsible?: boolean; panels?: PanelConfig[] }
bun run lint
bun run format:check
bun run type-check
bun test
bun run buildMIT