Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>React Flow TracerNew to Visual Studio Code? Get it now.
React Flow Tracer

React Flow Tracer

zaid Alfaqeeh

|
1 install
| (0) | Free
Trace what actually happens in your React / React Native code when a user performs a UI action. Static analysis, offline, no AI.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

React Flow Tracer

What actually happens when a user taps this button?

React Flow Tracer answers that question by statically analyzing your React and React Native code. Put your cursor on a JSX event handler, run one command, and get an interactive tree of everything that code path can reach — function calls across files, network requests, navigation, state updates and other side effects — with every node clickable straight to its source.

Static analysis only. This extension reads your code; it never runs it. Results describe what the code can do, not what it will do at runtime. See Limitations.


The problem

You open an unfamiliar screen and find this:

<Button title="Checkout" onPress={checkout} />

Answering "what does pressing this do?" means a manual crawl: jump to checkout, discover it comes from useCheckout(), open that hook, follow createOrder, open the service, find the HTTP call. Four files later you have the answer — and you repeat the whole thing for the next button.

Go-to-definition answers one hop. Call hierarchy shows callers, not behaviour. Neither tells you "this button posts to /orders, dispatches a Redux action and navigates to Success."

The solution

One command produces the whole picture:

USER ACTION: Checkout (Button)

onPress
└── checkout()
    └── createOrder()
        └── POST /orders

The sidebar adds a summary and analysis warnings:

Summary
  API Calls          1
  State Changes      1
  Navigation         1
  Side Effects       2

Warnings
  Static analysis detected a potential network request: POST /orders.

Every row opens the exact file, line and column it came from.


Reverse tracing — "what can trigger this?"

Forward tracing answers "what does this button do?". Reverse tracing answers the opposite question, which is usually the harder one when you are about to change shared code:

Which user actions can reach this function or side effect?

Put the cursor on a function, a call, or a detected side effect and run React Flow Tracer: Find User Actions:

Target
  POST /orders

User Actions
  CheckoutScreen · Checkout Button
    onPress
      checkout()
        createOrder()
          POST /orders

  QuickOrderScreen · Place Order
    onClick
      placeOrder()
        createOrder()
          POST /orders

Summary
  User Actions     2
  Paths            2
  Internal Nodes   3

Useful before touching a shared service: you can see every entry point that reaches it before you change its behaviour.

What you can start from

A function declaration, a call to one of your functions, or any detected side-effect node — API (fetch, axios, service clients), navigation, Redux dispatch, TanStack Query, storage, and analytics.

How it works

Reverse tracing is not a text or name search, and it is not a second graph. It:

  1. resolves the cursor to a declaration or call site using the TypeScript type checker;
  2. walks the import graph upwards from that file to find which modules can reach it (semantic module resolution, not a workspace scan);
  3. runs the existing forward trace engine from each user action found in those modules;
  4. keeps the traces whose nodes contain the target, and reports the path from the forward trace's own edges.

Because every reported path is produced by the forward engine, forward and reverse results cannot disagree.

Reverse tracing limitations

  • It finds statically resolvable user-action paths. It does not claim to find all callers: a caller reached only through dynamic dispatch, a runtime-generated handler, or an unresolvable alias is not reported.
  • An action whose handler cannot be resolved is skipped rather than guessed.
  • The same depth, node, timeout and cancellation limits as forward tracing apply, and a truncated search says so explicitly.
  • The first reverse trace in a project loads that project's files (roughly half a second on a small project); subsequent reverse traces in the same project reuse it and are typically an order of magnitude faster.

When nothing is found, the result distinguishes the two cases rather than showing a bare "no results":

  • "No user action was found that can be statically traced to this target."
  • "No React/React Native user action root was found in the reachable callers." — callers exist, but none of them is a UI event handler.

Installation

From the Marketplace — search for React Flow Tracer in the Extensions view, or:

code --install-extension zaidAlfaqeeh.react-flow-tracer

From a VSIX

npx @vscode/vsce package
code --install-extension react-flow-tracer-0.3.3.vsix

From source

git clone https://github.com/zaid-alfaqeeh/react-action-trace.git
cd react-action-trace
npm install
npm run compile

Then press F5 in VS Code to launch an Extension Development Host.


Usage

  1. Open a .tsx, .jsx, .ts or .js file.
  2. Put the cursor on a JSX event handler — the prop name, the handler, or the element itself all work.
  3. Trigger a trace in any of these ways:
    • Command Palette → React Flow Tracer: Trace Current Action
    • Right-click in the editor → Trace React Action
    • Click the Trace Action CodeLens above the handler
  4. Read the result in the React Flow Tracer sidebar.
  5. Run React Flow Tracer: Open Graph for the visual view.

To go the other way, put the cursor on a function or side effect and run React Flow Tracer: Find User Actions (also on the editor context menu). Both results appear in the same sidebar and both can be opened in the graph.

Commands

Command What it does
React Flow Tracer: Trace Current Action Forward: what this UI action does
React Flow Tracer: Find User Actions Reverse: which UI actions reach this
React Flow Tracer: Open Graph Graph view of the current result
React Flow Tracer: Copy Trace as Text Copy the current result
React Flow Tracer: Re-run Trace Re-run the last forward trace
React Flow Tracer: Clear Trace Clear the sidebar
React Flow Tracer: Show Diagnostics Output Open the output channel

Screenshots

Screenshot and demo capture is pending for this release. No screenshot images are included; none are referenced here to avoid broken links.


Supported frameworks

"Support" below means event detection and call tracing are verified by tests. It does not mean every API of that framework is modelled — see Limitations.

Framework Support
React (web) Events on DOM elements and components; handler resolution
React Native Events on the core components listed below; handler resolution
TypeScript / TSX Resolution via the TypeScript type checker
JavaScript / JSX Works without a tsconfig.json; resolution is weaker
Redux / Redux Toolkit dispatch(...) and action creators detected
TanStack Query (v4 & v5) Cache calls and mutate() → mutationFn detected
React Navigation navigate / push / replace / goBack detected
Next.js / React Router router.push / replace detected
Monorepos (pnpm, npm, yarn, Turborepo, Nx) Nearest-tsconfig project discovery

Not modelled as distinct behaviours (they appear as ordinary function calls, not as typed side effects): React Native platform APIs such as Linking, Vibration, Alert, Share, Clipboard, Permissions and native modules; React Context providers; GraphQL clients; WebSockets.

No framework is a hard dependency. Everything is detected from your source.

Supported events

React web — onClick, onDoubleClick, onChange, onInput, onSubmit, onReset, onFocus, onBlur, onKeyDown, onKeyUp, onMouseDown, onMouseEnter, onMouseLeave, onDrop, onScroll, onSelect and more, on button, input, form, select, textarea, a and other elements.

React Native — onPress, onPressIn, onPressOut, onLongPress, onChangeText, onSubmitEditing, onEndEditing, onValueChange, onRefresh, onEndReached, onScroll, onSelectionChange and more, on Pressable, TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, Button, TextInput, ScrollView, FlatList, SectionList, Switch, Modal and others.

Any prop matching the onXxx convention is also traceable, so custom components work without configuration.

Handler shapes

<Button onPress={handleCheckout} />              // direct reference
<Button onPress={() => handleCheckout()} />      // inline wrapper
<Button onPress={() => handleCheckout(order.id)} /> // arguments shown
<Button onPress={async () => { await save(); }} />  // inline body
const handle = useCallback(() => checkout(), []);   // unwrapped
const { checkout } = useCheckout();                 // resolved through the hook

Supported analyzers

Analyzer Detects Example output
API fetch, axios.*, configurable service clients POST /orders
Navigation navigation.navigate/push/replace, router.push navigate -> Checkout
State useState / useReducer setters, verified against their declaration setEmail(value)
Redux dispatch(...) with action creators or objects dispatch(orderCreated())
Query invalidateQueries, setQueryData, mutate queryClient.invalidateQueries()
Storage localStorage, AsyncStorage, SecureStore, MMKV AsyncStorage.setItem("user.name")
Analytics Configurable providers and functions analytics.track: order_created

URLs, routes and event names are shown only when they are statically resolvable. Dynamic values are labelled honestly rather than guessed:

POST <dynamic URL>

Configuration

Setting Default Description
reactFlowTracer.enableCodeLens true Show the "Trace Action" CodeLens
reactFlowTracer.maxDepth 20 Maximum traversal depth
reactFlowTracer.maxNodes 300 Maximum nodes before truncation
reactFlowTracer.maxFileSize 1048576 Skip files larger than this (bytes)
reactFlowTracer.timeoutMs 10000 Per-trace time budget
reactFlowTracer.enableReactNative true Detect React Native events
reactFlowTracer.enableReactWeb true Detect React DOM events
reactFlowTracer.enableApiDetection true Detect network calls
reactFlowTracer.enableNavigationDetection true Detect navigation
reactFlowTracer.enableStateDetection true Detect state and storage
reactFlowTracer.apiClientNames ["api", "client", "http", ...] Identifiers treated as HTTP clients
reactFlowTracer.analyticsObjectNames ["analytics", "mixpanel", ...] Analytics provider objects
reactFlowTracer.analyticsFunctionNames ["track", "logEvent", ...] Analytics function names
reactFlowTracer.excludeGlobs ["**/node_modules/**", ...] Paths excluded from analysis

Teams with a custom API wrapper only need to add its name to apiClientNames for myHttp.post("/orders") to be recognised.


Architecture

src/
├── extension.ts              Activation, command and provider registration
├── commands/                 Command handlers (trace, graph, reveal)
├── analyzer/
│   ├── ActionDetector.ts     Finds JSX event handlers at a position
│   ├── TraceEngine.ts        Traversal, limits, cycle protection
│   ├── SymbolResolver.ts     Type-checker-backed cross-file resolution
│   ├── ProjectManager.ts     tsconfig discovery, project cache
│   ├── TraceService.ts       Facade used by the VS Code layer
│   ├── eventCatalog.ts       Event/component data tables
│   └── analyzers/            Pluggable behaviour analyzers
├── models/                   TraceNode, TraceEdge, ActionTrace (UI-independent)
├── providers/                TreeView and CodeLens providers
├── webview/                  Graph panel and its assets
├── utils/                    Config, logging, AST helpers
└── tests/                    Unit and VS Code integration tests

Design principles

  • Real analysis. The TypeScript compiler API (through ts-morph) does the resolution. Regex is never the primary mechanism.

  • Model/UI separation. models/ has no VS Code or ts-morph imports, so the engine is testable in plain Node and reusable in other front-ends.

  • Pluggable analyzers. Adding a behaviour means implementing one interface and registering it — the engine does not change:

    interface BehaviorAnalyzer {
      readonly id: string;
      canAnalyze(node: ts.Node, context: AnalysisContext): boolean;
      analyze(node: ts.Node, context: AnalysisContext): AnalysisResult;
    }
    
  • Lazy and cached. Projects are created per tsconfig and reused; files load on demand through import resolution rather than scanning the repo; a save invalidates only the file that changed.


Limitations

React Flow Tracer performs static analysis. It reports what the code could do, and it deliberately declines to guess.

  • No runtime guarantees. A branch that never executes still appears. The trace shows reachable code, not an execution log.
  • No conditional evaluation. Both sides of an if are traced; the engine does not evaluate conditions.
  • Dynamic dispatch is not resolved. handlers[key](), values from eval, and runtime-constructed callables are reported as unresolved rather than guessed.
  • Dynamic URLs are not reconstructed. fetch(`/users/${id}`) is shown as GET <dynamic URL> (with the pattern in the tooltip).
  • Library internals are not expanded. Calls into node_modules are marked as external and not traversed.
  • Class components are partially supported. Methods resolve, but this binding through decorators or HOCs may not.
  • Very large graphs are truncated at the configured limits, always with an explicit truncation node and warning — never silently.
  • Dynamic import() is detected and reported, not followed.
  • State detection depends on resolution. A setXxx call is reported as state only when it resolves to a useState/useReducer binding. In a plain JavaScript project where the symbol cannot be resolved, the naming convention is used as a fallback, so an unrelated setSomething() may be reported as state there.
  • Values returned from factory functions are not resolved. Given const handler = getHandler(), the trace reports handler as unresolved rather than guessing which function was returned.
  • Analyzer names are matched by identifier, so a local function that happens to be called track is treated as an analytics event. Adjust analyticsFunctionNames if this collides with your code.

When something cannot be resolved, the extension says so in the Warnings section rather than inventing a plausible answer.


Roadmap

  • [x] Reverse tracing: "which actions can reach this function?" (v0.2)
  • [ ] Context provider and prop-drilling resolution
  • [ ] Class component this.handler binding
  • [ ] GraphQL operation detection (Apollo, urql)
  • [ ] WebSocket and EventSource detection
  • [ ] Export a trace to Markdown or Mermaid
  • [ ] Workspace-wide action inventory
  • [ ] Optional AI layer for Explain Trace and Suggest Refactor (strictly opt-in; the core stays offline and deterministic)

Contributing

npm install
npm run compile     # build
npm run lint        # eslint
npm test            # unit tests (plain Node, no VS Code download)
npm run test:integration   # VS Code integration tests

The unit suite covers the analyzer and runs anywhere. The integration suite launches a real VS Code instance, so it needs network access on first run and an environment that can start Electron — a desktop session, or xvfb-run on Linux CI. In a headless or sandboxed shell it fails at launch with bad option: --extensionDevelopmentPath; that is the environment, not the tests. Pin a specific build with VSCODE_TEST_VERSION=1.96.0 if a release regresses.

Press F5 to launch the Extension Development Host against test-fixtures/cross-file.

Adding an analyzer

  1. Implement BehaviorAnalyzer in src/analyzer/analyzers/.
  2. Register it in createDefaultAnalyzers().
  3. Add a fixture under test-fixtures/ and a test in src/tests/unit/.

Analyzer order matters: the first canAnalyze match wins.

Conventions for warnings — use "detected", "potential" and "could not statically resolve". Never claim that code definitely runs. There is a test that enforces this.


Privacy and security

  • Works fully offline. No network requests.
  • No telemetry in the current release.
  • No source code leaves your machine.
  • No project code is executed — analysis is purely syntactic and semantic.
  • The graph webview runs under a strict Content Security Policy with a nonce, and loads only local resources.

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft