Skip to content

Common Pitfalls ​

Common mistakes when using AutoTracer and how to avoid them.

Overview ​

This guide covers frequent issues developers encounter when implementing AutoTracer, along with solutions and preventive measures.

ReactTracer Pitfalls ​

1. Forgetting to Initialize ReactTracer ​

Problem:

typescript
// ❌ Component traced but tracer not initialized
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

// No output in console!

Solution:

typescript
// pages/_app.tsx or main.tsx
async function bootstrap(): Promise<void> {
  const { reactTracer } = await import("@autotracer/react18");

  reactTracer({ enabled: true }); // Initialize first!

  await import("./app");
}

void bootstrap();

export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

Prevention: Always initialize tracers in your app's entry point before React renders.

2. Missing React DevTools Hook ​

Problem:

Error: React DevTools global hook not found

Solution:

Install React DevTools browser extension OR add DevTools hook shim:

typescript
// pages/_document.tsx (Next.js)
<script
  dangerouslySetInnerHTML={{
    __html: `
(function() {
  if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) return;
  var nextID = 0;
  window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
    renderers: new Map(),
    supportsFiber: true,
    inject: function(injected) { return nextID++; },
    onScheduleFiberRoot: function() {},
    onCommitFiberRoot: function() {},
    onCommitFiberUnmount: function() {}
  };
})();
    `,
  }}
/>

Prevention: In development, Next.js Fast Refresh provides the hook automatically. For production, use the browser extension.

3. Plugin Order Issues (Vite) ​

Problem:

typescript
// ❌ Wrong order
export default defineConfig({
  plugins: [
    react(), // React plugin first
    reactTracer.vite(), // Tracer plugin second - TOO LATE!
  ],
});

Solution:

typescript
// ✅ Correct order
export default defineConfig({
  plugins: [
    reactTracer.vite(), // Tracer plugin FIRST
    react(), // React plugin second
  ],
});

Prevention: Always place reactTracer.vite() before react() in the plugins array.

4. Tracing Server Components (Next.js App Router) ​

Problem:

typescript
// app/server-component.tsx
// NO "use client" directive

export function ServerComponent() {
  // This shouldn't be traced but is being transformed
  return <div>Server content</div>;
}

Solution:

javascript
// babel.config.js
{
  "plugins": [
    [
      "@autotracer/plugin-babel-react18",
      {
        "serverComponents": true  // Enable this!
      }
    ]
  ]
}

Prevention: Set serverComponents: true when using Next.js App Router.

5. High-Frequency Component Flooding Console ​

Problem:

typescript
// ❌ Component updates 60 times/second
function AnimationFrame() {
  const [frame, setFrame] = useState(0);

  useEffect(() => {
    const id = requestAnimationFrame(() => setFrame(frame + 1));
    return () => cancelAnimationFrame(id);
  });

  return <div>{frame}</div>;
}

// Console flooded with thousands of traces!

Solution:

typescript
// ✅ Disable tracing for this component
// @trace-disable
function AnimationFrame() {
  const [frame, setFrame] = useState(0);
  // No tracing overhead
}

Prevention: Use @trace-disable pragma for high-frequency components (animation, timers, frequent updates).

6. Forgetting labelState() for Prop Changes ​

Problem:

typescript
// Props change but not traced
function UserCard({ user }) {
  return <div>{user.name}</div>;
}

// No output when user prop changes

Solution:

typescript
import { useReactTracer, labelState } from "@autotracer/react18";

function UserCard({ user }) {
  useReactTracer();
  labelState({ user }, "UserCard"); // Label props explicitly

  return <div>{user.name}</div>;
}

Prevention: Build plugins automatically add labelState() for props. If manually instrumenting, don't forget props.

FlowTracer Pitfalls ​

1. Forgetting Runtime Import ​

Problem:

typescript
// ❌ Plugin configured but runtime not imported
// babel.config.js has @autotracer/plugin-babel-flow

// app.tsx
function App() {
  processData(); // Not traced!
}

// No output in console

Solution:

typescript
// ✅ Initialize the intended Flow runtime entry before the app loads
async function bootstrap(): Promise<void> {
  await import("@autotracer/flow");

  await import("./app");
}

void bootstrap();

function App() {
  processData(); // Now traced!
}

Prevention: Always import the intended Flow runtime entry in your app's entry point. Use @autotracer/flow for immediate startup. Use @autotracer/flow/runtime for dormant startup and later browser control.

2. Tracing Everything ("*" Pattern) ​

Problem:

javascript
// ❌ Traces ALL functions - massive overhead
{
  "include": {
    "functions": ["*"]  // Thousands of functions traced!
  }
}

// Console flooded, app slow

Solution:

javascript
// ✅ Use specific patterns
{
  "include": {
    "functions": ["handle*", "on*", "fetch*", "process*"]
  }
}

// Only relevant functions traced

Prevention: Start with specific patterns. Add more as needed.

3. Path Pattern Mistakes ​

Problem:

javascript
// ❌ Wrong pattern - misses files
{
  "include": {
    "paths": ["src/*"]  // Only matches direct children, not nested
  }
}

Solution:

javascript
// ✅ Correct pattern - matches all nested files
{
  "include": {
    "paths": ["src/**"]  // Matches src/ and all subdirectories
  }
}

Prevention: Use ** for recursive matching, * for single-level matching.

4. Circular Reference in Logged Objects ​

Problem:

javascript
// Object with circular reference
const obj = { self: null };
obj.self = obj;

function process(data) {
  // Recursive serializers and ad-hoc debug formatting can break on cyclic data.
  return data;
}

process(obj);
// Any naive recursive serializer can fail here.

Solution:

AutoTracer does not expose a public serializeObject(...) helper. If you need manual inspection here, project the value to the few fields you actually need before logging it, and use @autotracer/logger only for the logging path itself.

Prevention: Avoid logging huge nested objects in hot paths, and avoid enabling FlowTracer param/return logging for large values.

5. Not Using Dormant Mode in Restricted Test/QA Deployments ​

Problem:

typescript
// ❌ Active tracing in a deployed QA environment
await import("@autotracer/flow"); // Always logging by default

// Performance overhead, console clutter

Solution:

typescript
// ✅ Use dormant mode
await import("@autotracer/flow/runtime");

// In browser-based internal web apps, start tracing from the Dashboard when it is mounted.
// In non-Dashboard setups, activate when needed:
globalThis.autoTracer.flowTracer.start();

Prevention: Use @autotracer/flow/runtime for restricted staging and QA deployments when tracing should stay dormant until you activate it. In browser-based internal web apps, use the Dashboard as the normal control surface when that workflow is mounted.

Configuration Pitfalls ​

1. Babel Config Not Loading (Next.js) ​

Problem:

javascript
// .babelrc
{
  "plugins": ["@autotracer/plugin-babel-react18"]
}

// Config not applied in Next.js!

Solution:

javascript
// Use babel.config.js instead
module.exports = {
  presets: ["next/babel"],
  plugins: ["@autotracer/plugin-babel-react18"],
};

Prevention: Next.js requires babel.config.js, not .babelrc.

2. Missing Peer Dependencies ​

Problem:

bash
# Install plugin but not runtime
pnpm add -D @autotracer/plugin-vite-flow @babel/core @babel/preset-typescript

# Error: Cannot find module '@autotracer/flow'

Solution:

bash
# Install runtime plus the plugin peer dependencies
pnpm add @autotracer/flow
pnpm add -D @autotracer/plugin-vite-flow @babel/core @babel/preset-typescript

Prevention: Install @autotracer/flow plus the Vite plugin peer dependencies. @autotracer/logger is installed automatically with @autotracer/flow.

3. Environment Variable Name Mismatch ​

Problem:

typescript
// .env
ENABLE_TRACING = true;

// App checks a different variable (never matches)
if (process.env.ENABLE_AUTOTRACER !== "true") {
  // Never runs
}

Solution:

Make the variable name consistent, and evaluate it in the right place:

  • Prefer reading flags in build configuration (e.g. vite.config.ts, babel.config.js) and passing booleans into plugin options.
  • Avoid relying on client-exposed env vars unless you intentionally want the value in the browser bundle.

Prevention: Treat tracing enablement as a build-time concern (instrumentation on/off), and treat dormant mode as a runtime concern. In browser-based internal web apps, use the Dashboard when that workflow is mounted. In non-Dashboard setups, use globalThis.autoTracer.flowTracer.start() / globalThis.autoTracer.flowTracer.stop().

4. TypeScript Config Excluding Plugin Files ​

Problem:

json
// tsconfig.json
{
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.config.*"] // Excludes vite.config.ts!
}

// TypeScript errors in vite.config.ts

Solution:

json
{
  "include": ["src/**/*", "*.config.ts"],
  "exclude": ["node_modules"]
}

Prevention: Ensure config files included in TypeScript compilation.

Build and Performance Pitfalls ​

1. Not Disabling in Production ​

Problem:

typescript
// ❌ Tracing enabled in production build
export default defineConfig({
  plugins: [
    reactTracer.vite({ inject: true }), // Always enabled!
  ],
});

Solution:

typescript
// ✅ Conditional based on mode
export default defineConfig(({ mode }) => ({
  plugins: [reactTracer.vite({ inject: mode === "development" })],
}));

Prevention: Use mode or environment variables to control injection.

2. Large Bundle Size ​

Problem:

bash
# Bundle analysis shows large tracing libraries in production
@autotracer/react18: 45 KB
@autotracer/flow: 38 KB

Solution:

typescript
// Dynamic import (dev only)
if (process.env.NODE_ENV === "development") {
  const { reactTracer } = await import("@autotracer/react18");
  reactTracer({ enabled: true });
}

Prevention: Use conditional imports or disable plugins in production builds.

3. Slow Build Times ​

Problem:

bash
# Build takes 2x longer with tracing plugins
pnpm build  # 120s instead of 60s

Solution:

javascript
// Limit transformation scope
{
  "include": {
    "paths": ["src/features/debug-feature/**"]  // Not src/**
  }
}

Prevention: Use specific include patterns to reduce files processed.

4. Using buildWithWorkspaceLibs for Source Libraries ​

Problem:

A monorepo has a source-only workspace library (TypeScript, no dist/). The developer enables buildWithWorkspaceLibs: true to fix a Rollup resolution error:

typescript
// ❌ Wrong tool for source libs
reactTracer.vite({
  inject: mode === "development",
  buildWithWorkspaceLibs: true, // Overkill — removes React from bundle!
});

This externalisess React from the entire app bundle and replaces it with a UMD global loaded from unpkg.com. The app now breaks if the CDN is unreachable, the CSP blocks it, or the network is slow.

Solution:

Use resolve.alias instead — it redirects the import at resolution time and has zero runtime side-effects:

typescript
// ✅ Correct tool for source libs
import { resolve } from "path";

reactTracer.vite({
  inject: mode === "development",
  // no buildWithWorkspaceLibs
});

// in defineConfig:
resolve: {
  alias: {
    "@autotracer/react18": resolve(
      __dirname,
      "../../node_modules/@autotracer/react18",
    ),
  },
},

React stays in your normal bundle. No CDN dependency. No CSP risk.

When to use buildWithWorkspaceLibs: Only when the workspace library is a pre-built package (it has its own dist/) and @autotracer/react18 is already externalised from that dist/.

Prevention: See Monorepo Setup for the full decision tree.

Runtime Pitfalls ​

1. Hydration Mismatch (Next.js) ​

Problem:

typescript
// ❌ Tracer initialized during render
function App({ Component }) {
  reactTracer(); // Causes hydration error!
  return <Component />;
}

Solution:

typescript
// ✅ Initialize in useEffect
function App({ Component }) {
  useEffect(() => {
    reactTracer({ enabled: true }); // After hydration
  }, []);
  return <Component />;
}

Prevention: Initialize tracers in useEffect or outside component render.

2. Memory Leaks from Console Output ​

Problem:

javascript
// Console retains all logged objects
function processLargeData() {
  const hugeArray = new Array(1000000);
  // FlowTracer logs hugeArray - held in memory!
  return hugeArray.map(process);
}

// After many calls, memory usage explodes

Solution:

javascript
// Exclude memory-intensive functions
{
  "exclude": {
    "functions": ["processLargeData", "*Batch", "*Bulk"]
  }
}

// Or clear console periodically
setInterval(() => console.clear(), 60000);

Prevention: Exclude functions handling large data structures.

3. Tracer Initialization Order ​

Problem:

typescript
// ❌ Wrong order
import { useReactTracer } from "@autotracer/react18";

function Component() {
  useReactTracer();
  const [state] = useState(0);
  return <div>{state}</div>;
}

// No output for state!

Solution:

Build plugins automatically inject useReactTracer() before hooks. Manual instrumentation must follow same order:

typescript
// ✅ Correct order (done automatically by plugins)
function Component() {
  useReactTracer(); // FIRST
  const [state] = useState(0); // Then hooks
  return <div>{state}</div>;
}

Prevention: Use build plugins for automatic correct injection.

Integration Pitfalls ​

1. Mixing Vite and Babel Plugins ​

Problem:

typescript
// vite.config.ts with Vite plugin
plugins: [reactTracer.vite()];

// babel.config.js also has Babel plugin
plugins: ["@autotracer/plugin-babel-react18"];

// Both plugins applied - double instrumentation!

Solution:

Use either Vite plugin or Babel plugin, not both.

Prevention:

  • Vite projects: Use @autotracer/plugin-vite-*
  • Next.js/CRA: Use @autotracer/plugin-babel-*

2. Conflicting ESLint Rules ​

Problem:

typescript
// ESLint error: React Hook "useReactTracer" is called in function "Component"
// that is neither a React function component nor a custom React Hook function

Solution:

json
// .eslintrc.json
{
  "rules": {
    "react-hooks/rules-of-hooks": [
      "error",
      {
        "additionalHooks": "useReactTracer"
      }
    ]
  }
}

Prevention: Configure ESLint to recognize useReactTracer as a hook.

Checklist: Avoiding Common Pitfalls ​

Setup ​

  • [ ] Tracers initialized in app entry point
  • [ ] React DevTools hook available (extension or shim)
  • [ ] Build plugins in correct order
  • [ ] Runtime packages imported
  • [ ] Peer dependencies installed

Configuration ​

  • [ ] Specific function/path patterns (not "*")
  • [ ] serverComponents: true for Next.js App Router
  • [ ] Environment-specific settings
  • [ ] Exclude test files
  • [ ] Disable in production builds

Performance ​

  • [ ] High-frequency components excluded
  • [ ] Dormant mode for staging/production
  • [ ] Bundle size checked
  • [ ] Memory-intensive functions excluded
  • [ ] Console cleared periodically if needed
  • [ ] Monorepo source libs use resolve.alias, not buildWithWorkspaceLibs

Integration ​

  • [ ] Only one plugin type (Vite OR Babel, not both)
  • [ ] ESLint configured for useReactTracer
  • [ ] TypeScript config includes config files
  • [ ] Environment variable naming correct

Released under the MIT License.