From Prompt to Production: Documenting the AI Builder’s Journey.
Handing an AI code agent unrestricted access to your React repository usually ends in one of two ways: a beautifully refactored application, or a subtle hallucination that silently breaks your production routing.
Code agents like Aider, Cursor, or Claude Code are incredibly powerful for untangling legacy spaghetti code. But because they lack visual context—they can't "see" the browser rendering—they will often rip out crucial CSS classes, misplace DOM nodes, or accidentally drop edge-case conditional renders.
The only way to safely use AI to refactor frontend code is by treating your test suite as a concrete wall. Here is the practical workflow for using a CLI agent to refactor React components, utilizing automated tests to keep the LLM strictly on the rails.
The Scenario: The Bloated Dashboard Component
Imagine you are working on an internal SaaS dashboard. You have a UserDashboard.tsx file that has grown to 800 lines. It mixes raw fetch calls, complex useEffect data formatting, and the actual UI rendering.
You want to use an agent to split this into:
A custom hook (useUserDashboard.ts) for data fetching.
Several pure, isolated UI components.
If you just tell the agent "Refactor this file," it will guess at the intended behavior and likely break your props API. Instead, we are going to force the agent into a strict Test-Driven Development (TDD) loop.
How it Works: The Auto-Test Agent Loop
Instead of reviewing every line the AI writes manually, we configure the agent to run our test suite (e.g., Vitest or Jest) after every single file modification. If the tests fail, the agent reads the stderr output, feeds the stack trace back into its context window, and attempts a fix. It cannot "finish" the task until the tests pass green.
Here is the architectural flow:
graph TD
A[Prompt Agent to Refactor] --> B[Agent Edits Code]
B --> C[Agent Triggers Test Suite]
C -->|Tests Pass| D[Commit & Await User]
C -->|Tests Fail| E[Read Stack Trace]
E --> B
This workflow requires two prerequisites:
Writing the tests before the refactor (or having the agent write them against the current working implementation).
Configuring the agent to hook into your headless test runner.
The Configuration (Code to Copy)
For this example, we will use Aider, a popular CLI-based coding agent, because it natively supports running shell commands to verify its own work and iterating on failures.
First, set up a project-specific configuration file so you don't have to pass flags every time. Create an .aider.conf.yml file in your root directory:
.aider.conf.yml
model: claude-3-5-sonnet-latest
Command to run tests heedlessly without watch mode
test-cmd: npx vitest run
Tell Aider to automatically feed test failures back to the LLM
auto-test: true
Optional: keep code formatting strictly aligned
lint-cmd: npx eslint --fix
auto-lint: true
Next, create a specific architectural prompt. Do not rely on the agent to guess your preferred React patterns. Store this in prompts/react-refactor.md:
React Refactoring Rules
When refactoring components, you must strictly adhere to these rules:
- Extract all data fetching and state management into a separate file ending in
.hook.ts. - The UI component should only receive props. It must not contain
useEffector direct API calls. - Do not modify, rename, or remove any existing
data-testidattributes. - Do not alter any Tailwind classes on the resulting DOM nodes. Now, kick off the refactor from your terminal. Notice we are explicitly loading the component, its test file, and the rules prompt into the context window: aider src/components/UserDashboard.tsx \ src/components/tests/UserDashboard.test.tsx \ --message-file prompts/react-refactor.md \ --message "Extract the data logic from UserDashboard.tsx into a custom hook. Ensure all tests still pass." The agent will read the files, write the hook, update the component, and automatically run npx vitest run. If it accidentally breaks a prop interface, Vitest will throw an error, Aider will read the stack trace, and it will fix the bug before handing control back to you.
Pitfalls and Gotchas
When using agents on frontends, beware of these common failure modes:
The Visual Disconnect: Tests don't catch everything. An agent might successfully extract a component and pass the logical tests, but accidentally delete a flex-col class, breaking the layout. Always use visual regression tools (like Chromatic) or manually inspect the UI in your browser before merging.
Context Window Bloat: Do not add your entire src/components/ folder to the agent's context. It will get confused, hallucinate imports, and slow down your loop. Only add the specific component, its direct children, and its test file.
Brittle Mocking: If your tests heavily rely on mocking the internal implementation rather than the output, the agent will fail. For example, if your test asserts expect(fetch).toHaveBeenCalled(), but the agent refactors the code to use React Query, the test fails even though the feature works. Test behaviors, not implementations.
Infinite Loops: Sometimes an agent gets stuck trying to fix the same failing test over and over, burning tokens. Always monitor the CLI output and hit Ctrl+C if it loops more than 3 times.
What to Try Next
Ready to leverage agents in your frontend workflow safely? Try these tightly scoped tasks:
The Accessibility Auto-Fixer: Run npx @axe-core/cli to generate a report of a11y violations on a specific component. Pipe that report into an agent with the source code and ask it to add the missing aria-labels and keyboard navigation handlers.
The Storybook Generator: Feed a complex UI component to an agent and ask it to generate a comprehensive Component.stories.tsx file, covering all visual variants, loading states, and edge cases.
The CSS-to-Tailwind Migration: Give the agent a component that uses legacy CSS modules or styled-components. Instruct it to convert all styling to inline Tailwind utility classes while maintaining the exact visual output, using your component tests as the validation layer.
Top comments (0)