UI StudioUI Studio
+
+
+
+
Back to Blog
EngineeringJanuary 20, 20262 min read

How We Made AI Coding 3x Faster

Eliminating the file search phase with exact component locations

Don
Don
Developer

The Problem

When you ask an AI coding agent to "make this button red," here's what typically happens:

  1. Agent receives your request with some HTML context
  2. Agent searches the codebase for files containing "button"
  3. Agent reads 3-5 potential matches
  4. Agent finally finds the right file
  5. 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:

typescript
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:

typescript
<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:

typescript
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 path
  • lineNumber — Exact line in the source
  • columnNumber — Exact column position

Step 2: Path Normalization

Bundlers like webpack and Turbopack transform file paths into internal formats:

typescript
/(app-pages-browser)/./components/ui/button.tsx
webpack://my-app/./src/Header.tsx
turbopack://[project]/app/page.tsx

We normalize these back to actual source paths:

typescript
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:

typescript
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:

typescript
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:

typescript
┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐
│  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:

typescript
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:

typescript
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)         ← grandparent

This 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:

typescript
components/ui/button.tsx:42:19
                         │  │
                         │  └── column 19
                         └── line 42

No 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:

typescript
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:

  1. Search for files (2-3 tool calls)
  2. Read candidate files (2-4 tool calls)
  3. Find the right component
  4. Make the change

After:

  1. Open file directly (1 tool call)
  2. 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:

typescript
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:

typescript
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:

typescript
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:

  1. Install the UI Studio extension
  2. Start your provider server:
    bash
    UISTUDIO_CWD=/your/project pnpm dev:cursor
  3. Open your React app in development mode
  4. 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.