How I Made My React Components 3x Smaller Using AI Prompts
As a React developer, I've always struggled with bloated components—files that started as simple UI elements but grew into 500+ line monsters handling state, API calls, and complex logic. Last month, I decided to tackle this using AI-assisted refactoring, and the results were staggering: I reduced my average component size by 67% while improving readability. Here's exactly how I did it.
The Problem: Component Bloat
Take this example component I had in a production e-commerce app:
function ProductCard({ product }) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [inventory, setInventory] = useState(null);
const [isWishlisted, setIsWishlisted] = useState(false);
const [zoomImage, setZoomImage] = useState(false);
useEffect(() => {
const fetchInventory = async () => {
setIsLoading(true);
try {
const response = await fetch(`/api/inventory/${product.id}`);
const data = await response.json();
setInventory(data.stock);
} catch (err) {
setError('Failed to load inventory');
} finally {
setIsLoading(false);
}
};
fetchInventory();
}, [product.id]);
const handleAddToCart = async () => {
// 15 lines of cart logic
};
const handleWishlistToggle = async () => {
// 12 lines of wishlist logic
};
// 40 more lines of helper functions and JSX
}
This 120-line component was doing too much: inventory checks, wishlist management, image zooming, and cart interactions—all while mixing presentational and business logic.
My AI-Powered Refactoring Strategy
I used a combination of ChatGPT-4 and Claude 3 to systematically break this down. Here was my prompt framework:
Identify extraction candidates:
"Analyze this React component and list all discrete responsibilities it handles, ranked by how easily they could be extracted to custom hooks or child components."Generate hook templates:
"Create a custom React hook for managing product inventory that includes loading states, error handling, and a refresh capability. Use TypeScript."
The AI suggested this inventory hook:
function useProductInventory(productId: string) {
const [state, setState] = useState<{
stock: number | null;
isLoading: boolean;
error: string | null;
}>({
stock: null,
isLoading: false,
error: null,
});
const fetchInventory = useCallback(async () => {
setState(prev => ({ ...prev, isLoading: true }));
try {
const response = await fetch(`/api/inventory/${productId}`);
const data = await response.json();
setState({ stock: data.stock, isLoading: false, error: null });
} catch (err) {
setState({ stock: null, isLoading: false, error: 'Inventory check failed' });
}
}, [productId]);
useEffect(() => {
fetchInventory();
}, [fetchInventory]);
return {
...state,
refresh: fetchInventory,
};
}
- Component decomposition: "Suggest a component structure where the ProductCard delegates image zoom functionality to a dedicated ProductImage component with gesture controls."
The AI proposed this separation:
function ProductImage({ src, alt }) {
const [isZoomed, setIsZoomed] = useState(false);
const handleZoomToggle = useCallback(() => {
setIsZoomed(!isZoomed);
}, [isZoomed]);
return (
<div className={`image-container ${isZoomed ? 'zoomed' : ''}`}>
<img
src={src}
alt={alt}
onClick={handleZoomToggle}
className="product-image"
/>
</div>
);
}
The Refactored Result
After applying similar extraction patterns for wishlist and cart logic, here's what remained in the main component:
function ProductCard({ product }) {
const { stock, isLoading: inventoryLoading } = useProductInventory(product.id);
const { isWishlisted, toggleWishlist } = useWishlist(product.id);
const { addToCart } = useCart();
return (
<div className="product-card">
<ProductImage src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.price}</p>
{inventoryLoading ? (
<Spinner size="small" />
) : (
<StockIndicator stock={stock} />
)}
<div className="action-buttons">
<WishlistButton
isActive={isWishlisted}
onClick={toggleWishlist}
/>
<AddToCartButton
onClick={() => addToCart(product.id)}
disabled={!stock}
/>
</div>
</div>
);
}
Key Improvements
- Line count reduction: From 120 → 38 lines (68% smaller)
- Reusability: Extracted hooks were used across 12 other components
- Testability: Each logical unit could now be tested in isolation
- Render performance: Reduced unnecessary re-renders by 40% (measured with React Profiler)
Lessons Learned
AI works best with concrete examples: Vague prompts like "make this better" yielded worse results than specific requests about state management or component structure.
Not all suggestions are gold: About 30% of AI-generated code needed manual tweaking—especially around error handling edge cases.
TypeScript matters: When I switched my prompts to include TypeScript requirements, the quality of hook suggestions improved significantly.
Iterative process works best: Breaking down the refactor into discrete steps (hooks first, then components) prevented overwhelm.
Conclusion
By combining AI's rapid prototyping capabilities with human judgment for final implementation, I transformed my React codebase from a tangled mess into a clean, maintainable architecture. The key was using AI not to write entire components, but to help identify and extract logical units—turning monolithic components into composed, focused pieces.
The results speak for themselves: faster load times, fewer bugs, and most importantly—components that are actually pleasant to work with. Next time you're facing a bloated React file, try this AI-assisted decomposition approach—you might be surprised how much cleaner your code can become.
⚡ Want the Full Prompt Library?
I compiled all of these patterns (plus 40+ more) into the Senior React Developer AI Cookbook — $19, instant download. Covers Server Actions, hydration debugging, component architecture, and real production prompts.
Browse all developer tools at apolloagmanager.github.io/apollo-ai-store
Top comments (0)