Skip to content
Prompt of the Day — Part 4 of 30

Prompt of the Day: Refactor a 200-Line Component into Smaller Pieces

Written by claude-sonnet-4 · Edited by claude-sonnet-4
refactoringreactcomponentscustom-hooksnextjsvibe-codingprompt-of-the-daycode-organizationtypescript

Series: Prompt of the Day — Part 4 of 30 | Track: Prompts | By Tom Hundley


The Component That Ate Your Codebase

Six months ago, a developer named Priya started vibe coding a SaaS dashboard with Cursor. She described what she wanted, the AI delivered, and she shipped it. Fast. Clean.

Then she had to add a filter to the data table.

She opened DashboardPage.tsx and found 247 lines of intertwined JSX, six useState calls, three useEffect hooks, two API fetches, a form handler, and a modal—all living in one function. The component had grown like a city without a zoning code. She spent four hours just understanding what she already had before writing a single line.

This is the most common trap in AI-assisted development. As one developer put it in a 2025 thread: "I might receive 200 lines of code that include hooks, providers, validation schemas. Although it works flawlessly, I find myself completely lost when I try to make modifications weeks later." A Lawfare Media analysis of vibe coding risks put it bluntly: "Coding agents often generate new instead of reusing or refactoring existing code, leading to code bloat and technical debt."

The good news: breaking a bloated component apart is exactly the kind of task AI does exceptionally well—if you give it the right instructions.


The Prompt

Refactor the component below into smaller, focused pieces. Follow these rules:

1. Extract each distinct section of JSX into its own component (e.g., <Header />, <FilterBar />, <DataTable />, <EmptyState />). Put each in its own file under components/<ComponentName>/<ComponentName>.tsx with a default export.

2. Extract all data-fetching logic (fetch calls, loading/error state) into a custom hook named use<Resource> (e.g., useOrders, useDashboard). Place it in hooks/use<Resource>.ts. Return { data, isLoading, error } from the hook.

3. Extract any form handlers, validation logic, or transformation functions into a utils file at utils/<feature>Utils.ts. These should be pure functions with no React dependencies.

4. Keep the parent component as a thin orchestrator: it imports the sub-components and hooks, handles layout, and passes props. It should be under 50 lines.

5. Do NOT change any behavior, UI, or business logic. This is a pure structural refactor.

6. After the refactored code, give me a file tree showing exactly where each new file lives.

Here is the component:

[PASTE YOUR COMPONENT HERE]

Why It Works

This prompt succeeds because it does four things a vague refactor request never does:

It gives the AI a decision framework. Without rules, the AI guesses at where to draw the lines. With explicit rules—JSX sections become components, hook logic becomes custom hooks, pure functions become utils—it produces consistent, predictable output.

It enforces the single-responsibility principle structurally. By asking for a file per component and a file per hook, you prevent the AI from "refactoring" by just renaming things inside the same file. Each concern gets a physical home.

It protects against behavior drift. Rule #5 is critical. Without it, the AI often improves your code—refactoring a useEffect, adding error boundaries, simplifying a fetch—which sounds great until you have a subtle regression in a feature you didn't know was coupled.

It produces a file tree. Asking for the output forces the AI to commit to a structure you can review before accepting it. You're auditing architecture, not just syntax.

What the output looks like

Given a bloated DashboardPage.tsx, the prompt produces something like:

Before — one 220-line file:

// DashboardPage.tsx — the monolith
export default function DashboardPage() {
  const [orders, setOrders] = useState<Order[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [filter, setFilter] = useState('all');
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [selectedOrder, setSelectedOrder] = useState<Order | null>(null);

  useEffect(() => {
    fetch('/api/orders')
      .then(r => r.json())
      .then(data => setOrders(data))
      .catch(err => setError(err.message))
      .finally(() => setIsLoading(false));
  }, []);

  const filteredOrders = orders.filter(o =>
    filter === 'all' ? true : o.status === filter
  );

  // ... 180 more lines of JSX, handlers, and modal logic
}

After — a clean orchestrator:

// DashboardPage.tsx — 40 lines, easy to scan
import { DashboardHeader } from '@/components/DashboardHeader/DashboardHeader';
import { OrderFilterBar } from '@/components/OrderFilterBar/OrderFilterBar';
import { OrderTable } from '@/components/OrderTable/OrderTable';
import { OrderDetailModal } from '@/components/OrderDetailModal/OrderDetailModal';
import { useOrders } from '@/hooks/useOrders';
import { filterOrders } from '@/utils/orderUtils';

export default function DashboardPage() {
  const { orders, isLoading, error } = useOrders();
  const [filter, setFilter] = useState('all');
  const [selectedOrder, setSelectedOrder] = useState<Order | null>(null);

  const filteredOrders = filterOrders(orders, filter);

  if (error) return <ErrorState message={error} />;

  return (
    <main>
      <DashboardHeader />
      <OrderFilterBar activeFilter={filter} onFilterChange={setFilter} />
      <OrderTable
        orders={filteredOrders}
        isLoading={isLoading}
        onSelectOrder={setSelectedOrder}
      />
      {selectedOrder && (
        <OrderDetailModal
          order={selectedOrder}
          onClose={() => setSelectedOrder(null)}
        />
      )}
    </main>
  );
}

As CodeScene's analysis of React component health explains, whenever you see a long list of useState or useEffect hooks in a single component, that's a signal—not a failure, just an opportunity to extract a custom hook that names the concept and groups related state together.


The Anti-Prompt

Don't write this:

Refactor this component to make it cleaner

Why it fails:

"Cleaner" is subjective. Without constraints, the AI will apply its own taste—which often means:

  • Renaming variables to be more descriptive (cosmetic, not structural)
  • Extracting one helper function while leaving the rest intact
  • Adding comments to explain the existing complexity instead of removing it
  • Or, worst case: restructuring in a way that changes subtle behavior

You'll get code that looks better on first read but has the same monolith problem. More dangerously, the AI might silently change logic while tidying—and without explicit instruction to preserve behavior, you won't catch it until a user reports a bug.

Vague prompts produce vague results. Architecture decisions require explicit constraints.


Variations

When you can't touch the parent component (legacy codebase):

Refactor the JSX and logic inside this component WITHOUT changing the component's
external interface (same props, same exported name). Extract internal sub-components
as unexported functions in the same file, and pull all useEffect/useState groups
into custom hooks in a sibling hooks/ folder. Keep all current prop signatures intact.

When you want Next.js Server Component boundaries respected:

Refactor this component while preserving Next.js App Router constraints:
- Any component that uses useState, useEffect, or event handlers must be a
  Client Component with 'use client' at the top
- Any component that only renders data (no interactivity) should be a
  Server Component (no 'use client' directive)
- Data fetching that can move to a Server Component should use async/await
  directly, not useEffect + fetch
Show me which components are Server vs Client and why.

When you want a complexity audit first:

Before refactoring, analyze this component and give me:
1. A list of distinct responsibilities this component currently owns
2. Which useState calls are related to each other (group them)
3. Which useEffect calls could become custom hooks
4. Your recommended file structure before writing any code

Wait for my approval before writing any refactored code.

This last variation is powerful. Getting the AI to plan before it codes gives you a checkpoint where you can redirect before it goes down the wrong path.


Your Checklist

  • Identify your largest component (check for files over 150 lines in your app/ or components/ folder)
  • Run the audit variation first—get the plan before the code
  • Review the proposed file structure and confirm it matches how your team thinks about the feature
  • Run the full refactor prompt, paste the component
  • Verify the file tree output matches what was proposed
  • Open a git diff and confirm: no behavior changed, only structure
  • Run your test suite (if you have one) or manually click through the affected UI
  • Check that each new custom hook has a name that describes what it does, not how it does it (useOrders, not useFetchAndSetOrdersArray)

Ask The Guild

Community prompt for this week:

What's the biggest component you've had to tame—AI-generated or otherwise? How many lines was it, and what finally convinced you to break it apart? Drop your story in the comments, especially if you used a prompt to do it.

Bonus: share the audit output if you ran the "analyze before refactor" variation. What did the AI identify that you hadn't noticed?

Copy A Prompt Next

Review and debug

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

23

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Working With AI ToolsWorking With AI Tools

v0 by Vercel — UI Components From a Text Prompt

Generate production-ready UI components with v0 and integrate them into your projects.

Preview
"I want v0 to generate a React component for this screen:
[describe the UI, data fields, visual style, empty state, loading state, and mobile behavior]
The component must:
1. work in a Next.js + Tailwind project
2. be easy to wire to real data later
Prompt Engineering

Turn this workflow advice into a durable operating system

Prompt and workflow posts are the quick win. The learning paths turn them into a durable operating model for tools, prompts, and agent supervision.

Best Next Path

Working With AI Tools

Explorer · Free

Turn ad hoc prompting into a repeatable workflow with better tool choice, stronger prompting, and safer day-to-day AI habits.

23 lessonsIncluded in the free Explorer plan

Need the free route first?

Start with Foundations for AI-Assisted Builders if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.