The Problem
When you ask an AI coding agent to "make this button red," here's what typically happens:
- Agent receives your request with some HTML context
- Agent searches the codebase for files containing "button"
- Agent reads 3-5 potential matches
- Agent finally finds the right file
- Agent makes the change
Steps 2-4 can take 60-70% of the total execution time. The agent is essentially playing detective, trying to figure out where your code lives.
We thought: what if we just told the agent exactly where to look?
The Insight
React already knows where every component comes from. When you see an error in React, you get a stack trace like this:
Error: Something went wrong
at Button (components/ui/button.tsx:42:19)
at Card (components/card.tsx:18:5)
at HomePage (app/page.tsx:12:3)React maintains this information internally through its fiber tree. Every rendered component has a reference back to its source file, line number, and even column number.
The question was: could we extract this information and give it to the AI agent?
The Solution
We built a system that intercepts React's internal component tree using Bippy, a library for React fiber introspection. When you select an element in UI Studio, we now return context that looks like this:
<button class="px-4 py-2 bg-blue-500..." type="button">
Click me
</button>
in Button (at components/ui/button.tsx:42:19)
in Card (at components/card.tsx:18:5)
in HomePage (at app/page.tsx:12:3)This format is intentionally modeled after React's error stack traces. AI models have seen millions of these during training, so they understand the format intuitively.
How It Works
Step 1: Fiber Tree Access
When the extension loads on a page, we inject a script that instruments React's internals:
import { instrument, getFiberFromHostInstance, getOwnerStack } from 'bippy'
// Initialize before React loads
safelyInstallRDTHook()
// Get component info from any DOM element
const fiber = getFiberFromHostInstance(element)
const frames = await getOwnerStack(fiber)The getOwnerStack function walks up the fiber tree and returns an array of stack frames, each containing:
functionName— The component name (e.g., "Button")fileName— The source file pathlineNumber— Exact line in the sourcecolumnNumber— Exact column position
Step 2: Path Normalization
Bundlers like webpack and Turbopack transform file paths into internal formats:
/(app-pages-browser)/./components/ui/button.tsx
webpack://my-app/./src/Header.tsx
turbopack://[project]/app/page.tsxWe normalize these back to actual source paths:
const BUNDLER_PREFIXES = [
/^\(app-pages-browser\)\//,
/^webpack:\/\/[^/]*\//,
/^turbopack:\/\/\[project\]\//,
// ... more patterns
]
for (const prefix of BUNDLER_PREFIXES) {
if (prefix.test(filePath)) {
filePath = filePath.replace(prefix, '')
break
}
}Step 3: Context Formatting
We format the context to look like a React error stack:
export const formatContextFromStack = (
htmlPreview: string,
frames: StackFrame[],
maxLines: number = 3
): string => {
const stackContext: string[] = []
for (const frame of frames) {
if (stackContext.length >= maxLines) break
// Format: "in ComponentName (at path/file.tsx:line:col)"
let line = `\n in ${frame.functionName} (at `
line += `${normalizedPath}:${frame.lineNumber}:${frame.columnNumber})`
stackContext.push(line)
}
return `${htmlPreview}${stackContext.join('')}`
}Step 4: Agent Directive
Finally, we add an explicit instruction to the agent prompt:
const directive = `IMPORTANT: The component stack below shows exact file paths and line:column numbers.
Open the specified file directly using the Read tool - do NOT search for files.
For example, if you see "components/ui/button.tsx:42:5", open that file immediately at line 42.
Make the requested changes without asking for confirmation.`This tells the agent to skip the search phase entirely and go straight to the file.
The Full Flow
Here's what happens when you select an element and send a prompt:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Chrome │ │ Provider │ │ AI Agent │
│ Extension │────▶│ Server │────▶│ (Cursor/ │
│ │ │ │ │ Claude) │
│ │ │ │ │ │
│ 1. Click elem │ │ 2. Add file │ │ 3. Open │
│ 2. Get fiber │ │ directive │ │ file │
│ 3. Format ctx │ │ 3. Stream to │ │ directly│
│ │ │ agent │ │ 4. Edit │
└─────────────────┘ └──────────────────┘ └─────────────┘The agent receives:
Make this button red
IMPORTANT: The component stack below shows exact file paths...
<button class="px-4 py-2 bg-blue-500...">
Click me
</button>
in Button (at components/ui/button.tsx:42:19)
in Card (at components/card.tsx:18:5)
in HomePage (at app/page.tsx:12:3)And immediately knows to open components/ui/button.tsx at line 42.
Why This Format?
We chose the React error stack format for several reasons:
1. Familiarity
AI models have seen this format millions of times in training data. Every React error, every Stack Overflow question about React includes stack traces in this format. The model doesn't need to learn a new syntax.
2. Hierarchical Context
The stack shows not just the immediate component, but its parents:
in Button (at components/ui/button.tsx:42:19)
in Card (at components/card.tsx:18:5) ← parent
in HomePage (at app/page.tsx:12:3) ← grandparentThis helps the agent understand where the component is used, which can be important for changes that affect props or styling inheritance.
3. Precise Targeting
Line and column numbers let the agent jump to the exact location:
components/ui/button.tsx:42:19
│ │
│ └── column 19
└── line 42No scrolling, no searching within the file.
Filtering Internal Components
Not all components in the fiber tree are useful. React and Next.js have many internal components:
const NEXT_INTERNAL_COMPONENT_NAMES = new Set([
'InnerLayoutRouter',
'RedirectErrorBoundary',
'LoadingBoundary',
'ErrorBoundary',
'HotReload',
'AppRouter',
// ... many more
])
const REACT_INTERNAL_COMPONENT_NAMES = new Set([
'Suspense',
'Fragment',
'StrictMode',
'Profiler',
])We filter these out so the agent only sees user-defined components that are actually editable.
Results
Before this optimization, a typical "make this red" request would:
- Search for files (2-3 tool calls)
- Read candidate files (2-4 tool calls)
- Find the right component
- Make the change
After:
- Open file directly (1 tool call)
- Make the change
The search phase is eliminated entirely. In our testing, this reduces execution time by 2-3x for simple UI changes.
For more complex changes that involve multiple files, the improvement is even more significant because the agent starts from a known location and can navigate relative to it.
Edge Cases
Non-React Pages
For pages without React (or where instrumentation fails), we fall back to sending just the HTML with a header describing the element:
Element: button.px-4.py-2
Location: https://example.com/page
<button class="px-4 py-2">Click me</button>The agent will need to search in this case, but it still has class names and structure to guide it.
Library Components
Components from node_modules are marked as library components:
const isLibrary = filePath.includes('node_modules')We still show them in the stack for context, but the agent knows not to try editing files in node_modules.
Server Components
Next.js server components are detected and marked:
in ServerComponent (at Server)This tells the agent the component renders on the server, which affects how changes should be made.
Try It Yourself
The optimization is live in UI Studio today. To see it in action:
- Install the UI Studio extension
- Start your provider server:
bash
UISTUDIO_CWD=/your/project pnpm dev:cursor - Open your React app in development mode
- Click any element and watch the agent jump straight to the file
You can see the formatted context in your browser's console — look for messages from [UI Studio].
What's Next
We're exploring a few related optimizations:
- Prop extraction: Including component props in the context so the agent understands the component's API
- Style tracing: Showing which CSS classes come from which files (especially useful for Tailwind)
- Multi-file awareness: Pre-computing related files (tests, stories, types) so the agent can work across the codebase
The core insight — giving AI agents precise location information instead of making them search — applies beyond React. We're looking at similar approaches for Vue, Svelte, and even non-framework codebases.
Have questions or ideas? Reach out — we'd love to hear from you.