@autotracer/plugin-vite-react18
v1.0.0-alpha.48
Published
Vite plugin for auto-injecting useReactTracer via unplugin
Readme
@autotracer/plugin-vite-react18
Automatic React component tracing for Vite projects.
This Vite plugin automatically injects useReactTracer() hooks into your React components at build time, enabling automatic state and prop labeling without manual instrumentation.
What You Get
- Reliable component tracking - Marks components for tracing, ensuring they're tracked
- Perfect variable names - Extract state/prop variable names from your source code
- Better state change detection - More reliable than fiber-only traversal
- Development-focused - Control injection per environment
- Next-gen build tool - Instant HMR during development
- TypeScript native - Full type safety and inference
- JSX/TSX support - Automatic React component detection
- Variable name extraction - See readable state labels in traces
- Improved change detection - Correctly resolves top level state for nested hooks, complex hooks such as selectors or rtkquery mutations, or proxied states such as react hook form.
- Pragma support - Fine-grained opt-in/opt-out control at component level
- Include/Exclude patterns in plugin config for file inclusion/exclusion.
- Hook naming config to cover edge cases with esoteric hook naming schemes.
- Default handles ^use[A-Z].*
Installation
pnpm add @autotracer/react18
pnpm add -D @autotracer/plugin-vite-react18Usage
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { reactTracer } from "@autotracer/plugin-vite-react18";
export default defineConfig(({ mode }) => ({
plugins: [
reactTracer.vite({
inject: mode === "development",
mode: "opt-out",
importSource: "@autotracer/react18",
// Optional: seed the initial output mode before ReactTracer initializes
// outputMode: "devtools",
include: ["src/**/*.tsx"],
exclude: ["**/*.spec.*", "**/*.test.*"],
// Enable this when using React Server Components (e.g., Next.js App Router)
// to ensure we only inject into modules marked with "use client".
// serverComponents: true,
}),
react(),
],
}));Configuration Options
inject
Type: boolean
Default: true
Controls whether code injection is enabled. Use this to conditionally enable the plugin based on mode or environment:
// Cleaner syntax - inject only in development
reactTracer.vite({
inject: mode === "development",
mode: "opt-out",
// ...other options
});
// Alternative: always include plugin, control via environment
reactTracer.vite({
mode: "opt-out",
// ...other options
});
// Then use TRACE_INJECT=0 to disableWhen false, the plugin is effectively disabled and no transformations occur. This is useful for:
- Disabling injection in production builds
- Conditional tracing based on environment variables
- Testing with and without tracing enabled
outputMode
Type: "devtools" | "copy-paste" | undefined
Default: undefined
When set, the plugin injects a small head-prepend script into index.html to seed the initial output mode before your app runs.
Startup behavior:
- Sets
globalThis.__autoTracerInternal.outputModeto the configured value - If
globalThis.autoTraceris already available, callsglobalThis.autoTracer.setOutputMode(outputMode)
dashboardConfig
Type: Partial<DashboardConfig> | undefined
Default: undefined
Enables an in-app dashboard widget with hotkey controls for runtime tracer management. When set, the plugin automatically:
- Injects the dashboard configuration as
globalThis.__autoTracerDashboardConfig - Imports and mounts the dashboard at page load
Configuration Options:
interface DashboardConfig {
enabled: boolean; // Enable/disable dashboard (default: true)
hideByDefault: boolean; // Widget hidden on first load (default: false)
position: "bottom-right" | "bottom-left" | "top-right" | "top-left"; // Widget position (default: 'bottom-right')
hotkeys: {
toggleTracing: string; // Hotkey to start/stop all tracers (default: 'Ctrl+Shift+T')
toggleDashboard: string; // Hotkey to show/hide widget (default: 'Ctrl+Shift+D')
};
}Example:
reactTracer.vite({
mode: "opt-out",
dashboardConfig: {
enabled: true,
hideByDefault: false, // Show widget immediately
position: "bottom-right",
hotkeys: {
toggleTracing: "Alt+Shift+T", // Start/stop React tracing
toggleDashboard: "Alt+Shift+D", // Show/hide widget
},
},
});Smart Visibility:
- If
hideByDefault: true, widget is hidden on first load but persists visibility state to localStorage - Once a tracer starts (via hotkey or programmatically), widget auto-shows and remembers this state
- Useful for QA environments where you want the widget available but not intrusive by default
Dual Tracer Support:
- When both
@autotracer/plugin-vite-react18and@autotracer/plugin-vite-floware active, the dashboard controls both tracers Alt+Shift+Ttoggles React tracing and Flow tracing together
See @autotracer/dashboard for full documentation and API reference.
Dev-only behavior
The plugin can be controlled in multiple ways:
injectparameter: Set tofalseto disable injection programmaticallyTRACE_INJECT=0environment variable: Disables injection regardless of configuration- Production builds: Consider using
inject: mode === "development"to ensure tracing only runs during development
Mode vs. pragmas (precedence)
The transformer supports compile-time pragmas to control tracing at the component level.
mode: 'opt-in'— Only files matchingincludeare considered. Within those files, use// @tracebefore individual components to enable injection for that component. Files outsideincludeare not processed.mode: 'opt-out'— All files are eligible except those matchingexclude. Use// @trace-disablebefore individual components to disable tracing for that component.
Precedence rules:
- File-level
excludealways wins: excluded files are never processed. // @trace-disablebefore a component disables that component only// @tracebefore a component enables that component (in opt-in mode)
Example pragmas:
// Component-level disable in an opt-out project
// @trace-disable
export function MyComponent() {
/* Not traced */
}
// Component-level enable in an opt-in project
// @trace
export function AnotherComponent() {
/* Traced */
}Hook value labels
You can opt-in to labeling values returned from hooks for nicer console output:
reactTracer.vite({
labelHooks: ["useState", "useReducer", "useSelector", "useAppSelector"],
labelHooksPattern: "^use[A-Z].*",
});This augments built-ins (useState/useReducer) by labeling identifiers assigned from matching hook calls.
Theme File Support
The plugin automatically loads and injects theme files from your project root at build time, enabling personal color and icon customization without code changes.
How It Works
The plugin searches for theme files in your project root directory:
- Base theme:
*react-theme.json(e.g.,react-theme.json,colorblind-react-theme.json) - Light mode override:
*react-theme-light.json - Dark mode override:
*react-theme-dark.json
Found themes are automatically injected as globalThis.__REACTTRACER_THEME__ before your app loads.
Quick Example
Create react-theme-light.json in your project root:
{
"definitiveRender": {
"icon": "🔄",
"lightMode": { "text": "#0066cc" }
},
"error": {
"icon": "❌",
"lightMode": { "text": "#cc0000", "background": "#fff5f5" }
}
}Add to .gitignore for personal customization:
# Personal react tracer themes
*react-theme.json
*react-theme-light.json
*react-theme-dark.jsonRestart your dev server to apply changes.
Theme Priority
Themes merge in this order (later overrides earlier):
- Built-in defaults
- Theme files (loaded by plugin)
- Programmatic config in
reactTracer({ colors: {...} })
This allows:
- Plugin loads team theme:
react-theme.jsoncommitted to repo - Developer adds personal override:
my-react-theme-light.jsonin.gitignore - Runtime override:
reactTracer({ colors: {...} })for testing
Complete Guide
See @autotracer/react18 THEME-FILES.md for:
- Complete theme file structure and schema
- Light/dark mode configuration
- Colorblind-friendly themes
- High-contrast themes
- Troubleshooting and best practices
Monorepo / Workspace Builds
When a workspace library is compiled by the host app's Rollup pass, the injected
import { useReactTracer } from "@autotracer/react18" can't be resolved from the
library's own package.json, causing a build error.
The right fix depends on how the library is consumed.
Source libraries (raw TypeScript) → resolve.alias
If the library is consumed as raw .tsx source (no dist/, "noEmit": true), use
resolve.alias in the host app to redirect @autotracer/react18 to the host's
installed copy. React stays bundled normally; no UMD globals, no CSP risk.
// host-app/vite.config.ts
import { resolve } from "path";
export default defineConfig(({ mode }) => ({
plugins: [reactTracer.vite({ inject: mode === "development", mode: "opt-out" }), react()],
resolve: {
alias: {
"@autotracer/react18": resolve(__dirname, "../../node_modules/@autotracer/react18"),
},
},
}));Pre-built packages (with own dist/) → buildWithWorkspaceLibs
If the library has its own build step and the host app imports from dist/, use
buildWithWorkspaceLibs: true. This externalises React and @autotracer/react18
from the entire app bundle and loads them via injected <script> tags.
⚠️ Side effect: React is removed from your app bundle. If the injected scripts fail to load (CDN outage, CSP, network error) the whole app breaks.
// host-app/vite.config.ts
const isQA = process.env.DEPLOY_ENV === "qa";
export default defineConfig(({ mode }) => ({
plugins: [
reactTracer.vite({
inject: mode === "development" || isQA,
mode: "opt-out",
buildWithWorkspaceLibs: isQA, // only for pre-built workspace deps
}),
react(),
],
}));Do not use
buildWithWorkspaceLibsfor source libs —resolve.aliasis simpler and has no bundle side-effects.
See the Monorepo Setup guide for a full decision tree, flowcharts, and the CSP / self-hosting walkthrough.
What buildWithWorkspaceLibs does automatically
- Externalises
@autotracer/react18,react, andreact-domas UMD globals. - Emits the ReactTracer UMD build alongside your output.
- Injects
<script>tags inindex.htmlto load React, ReactDOM, and ReactTracer before your app bundle. - Creates the React DevTools hook via an inline script so the tracer works in production without the browser extension.
Applies to vite build only — vite dev is unaffected.
Self-Hosting React UMD Files (CSP)
By default, buildWithWorkspaceLibs injects <script> tags pointing to unpkg.com.
If your CSP blocks external CDN requests, serve the files from your own origin.
Step 1 — Copy UMD files to your public folder:
node_modules/react/umd/react.production.min.js → public/vendor/react.production.min.js
node_modules/react-dom/umd/react-dom.production.min.js → public/vendor/react-dom.production.min.jsStep 2 — Configure the plugin:
reactTracer.vite({
inject: isQA,
buildWithWorkspaceLibs: isQA,
reactUmdSrc: "/vendor/react.production.min.js",
reactDomUmdSrc: "/vendor/react-dom.production.min.js",
});Security Considerations
Do Not Enable in Production
The ReactTracer plugin should never be enabled in production environments for the following reasons:
Information Disclosure - The plugin injects code that logs component state, props, and render details to the browser console, potentially exposing:
- User data and personal information (PII)
- Authentication state and session details
- Business logic and application structure
- Internal component implementation details
Performance Impact - Instrumentation and tracing generate significant overhead that degrades user experience
Bundle Size - Injected hooks and tracing code increase your bundle size unnecessarily
Attack Surface - The DevTools hook and tracing infrastructure are accessible to anyone with console access
Recommended Configuration
Always use environment-based configuration to disable the plugin in production:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { reactTracer } from "@autotracer/plugin-vite-react18";
export default defineConfig(({ mode }) => {
const isDev = mode === "development";
const isQA = process.env.DEPLOY_ENV === "qa";
return {
plugins: [
reactTracer.vite({
inject: isDev || isQA, // NEVER enable in production
mode: "opt-out",
include: ["src/**/*.tsx"],
exclude: ["**/*.spec.*", "**/*.test.*"],
}),
react(),
],
};
});For TEST/QA builds with workspace libraries:
When using buildWithWorkspaceLibs: true in TEST/QA environments, ensure the plugin inject option is properly configured:
// vite.config.ts - inject option controls whether tracing is enabled
const isQA = process.env.DEPLOY_ENV === "qa";
reactTracer.vite({
inject: isQA, // Only inject in QA, not production
buildWithWorkspaceLibs: isQA,
});Performance Considerations
Build-Time Impact
- Code transformation: ~10-50ms per file (depends on file size and complexity)
- Source map generation: ~5-20ms per file
- Total build overhead: typically < 5% of total build time
Runtime Impact
Per component with injection:
- Hook execution: ~1-2μs per render
- State/prop labeling: ~5-10μs per tracked value
Without injection (includeNonTrackedBranches: true):
- All components processed, significant overhead
- Not recommended for large applications
Optimization Tips
Use specific include patterns:
include: ["src/features/**/*.tsx"]; // Only feature componentsExclude test files:
exclude: ["**/*.spec.*", "**/*.test.*", "**/__tests__/**"];Use opt-in mode for selective tracing:
mode: "opt-in"; // Only trace components with // @trace pragmaDisable in production:
inject: mode === "development";
React Server Components (RSC)
If you're using an RSC-enabled framework (like Next.js App Router), set serverComponents: true in the plugin options. When enabled, the transform will:
Treat modules as Server Modules by default
Only inject into modules that have the top-level directive:
"use client";
This prevents injecting client-only hooks where they don't belong. All other transform rules (mode/include/exclude/pragma) still apply to Client Components.
License
MIT
