How to use Claude Code to refactor legacy React components efficiently
useEffect spaghetti and implicit state—are the worst things to touch manually. I’ve been using Claude Code (the CLI) to tear through a few of these in a project I'm maintaining, and the workflow is fundamentally different from using a chat interface like Cursor. Since Claude Code has direct terminal access and can execute commands, it can actually verify if the refactor broke the build before you even look at the diff.The trick to not hallucinating a complete rewrite that breaks everything is to use a "Test-Driven Refactor" loop. I don't just tell it to "clean up this file"; I force it to prove the logic remains the same.
Here is the exact sequence I use:
1. The "Baseline" Step
Before touching the code, I have Claude Code run the existing tests to ensure we are starting from a green state. If there are no tests, I make it write a few quick Vitest snapshots of the component's output for specific props.
# In the Claude Code terminal
/run npm test src/components/LegacyUserDashboard.tsx2. The "Atomic" Refactor Prompt
I avoid vague prompts. Instead of "refactor this," I give it a specific architectural target (e.g., moving logic to a custom hook). I use a prompt that emphasizes stability over "cleverness."
Extract all data fetching and state management logic from LegacyUserDashboard.tsx into a new hook called useUserDashboard.ts.
Keep the UI rendering in the main component.
Ensure no prop types are changed.
After the change, run the tests to verify no regressions.3. Handling the "Gotchas"
One thing I noticed is that Claude Code sometimes tries to upgrade dependencies or change the styling library (like swapping CSS Modules for Tailwind) if it thinks it's "better." To stop this, I add a .claudecodeconfig or a system prompt instruction that says: "Strictly adhere to existing project styling patterns; do not introduce new libraries unless explicitly asked."
4. The Verification Loop
The real power is the /run command. After the refactor, I don't just trust the code. I make it check for TypeScript errors across the whole project, because refactoring one component often breaks a consumer three folders away.
/run npx tsc --noEmitProductivity Gains
The biggest win here is the reduction in "context switching." In Cursor, I'm jumping between the chat, the file, and the terminal. With Claude Code, the loop is: Prompt → Edit → Test → Fix → Commit.
My recommended config for this workflow:
Enable git integration: Let it commit each small refactor step. If the useUserDashboard extraction works, commit it. If the next step (splitting the UI into sub-components) fails, you can revert one atomic change rather than undoing a massive AI rewrite.
Use the /compact command: When the conversation gets long and it starts forgetting the original constraints of the legacy component, I use /compact to clear the noise while keeping the essential context.
For anyone still doing this via copy-paste into a browser, the CLI approach is a massive leap because the AI sees the actual error output from your compiler, not just your description of the error.
All Replies (0)
No replies yet — be the first!
