Performance Optimization for AI-Generated Code
How to identify and fix performance problems in vibecoded apps — from React re-renders to bundle size to API latency.
Performance Optimization for AI-Generated Code
AI-generated code works. But "works" and "works fast" are different things. Here's how to find and fix the performance problems that AI introduces.
The Common Performance Pitfalls
1. Unnecessary Re-renders
This is the #1 performance issue in AI-generated React code. AI doesn't think about render cycles.
The problem:
// AI generates this — a new object on every render
function UserList({ users }) {
const sortedUsers = users.sort((a, b) => a.name.localeCompare(b.name));
// This creates a new reference every render, causing child re-renders
return <List items={sortedUsers} />;
}
The fix:
function UserList({ users }) {
const sortedUsers = useMemo(
() => [...users].sort((a, b) => a.name.localeCompare(b.name)),
[users]
);
return <List items={sortedUsers} />;
}
When to use useMemo:
- Expensive computations (sorting, filtering large arrays)
- Object/array references passed as props to memoized children
- Values used in dependency arrays of other hooks
When NOT to use useMemo:
- Simple string concatenation or arithmetic
- Primitives (numbers, strings, booleans) — they compare by value anyway
2. Bundle Size Bloat
AI loves importing full libraries when you need one function:
// AI generates this — imports ALL of lodash (70KB+)
import _ from "lodash";
const sorted = _.sortBy(users, "name");
// Better — import just what you need (4KB)
import sortBy from "lodash/sortBy";
// Best — write it yourself (0KB)
const sorted = [...users].sort((a, b) => a.name.localeCompare(b.name));
Check your bundle size:
npx next build
# Look for "First Load JS" in the output
# Anything over 100KB per page should be investigated
3. Missing Code Splitting
AI generates everything in one file. Real apps should split:
// AI generates this — loads the chart library even if user never opens the tab
import { BarChart } from "recharts";
// Better — load it only when needed
import dynamic from "next/dynamic";
const BarChart = dynamic(() => import("recharts").then((m) => m.BarChart), {
loading: () => <div className="h-64 bg-neutral-800 animate-pulse rounded-xl" />,
});
Use dynamic imports for:
- Charts and visualization libraries
- Rich text editors
- Code editors
- Maps
- Any component only visible after user interaction
4. N+1 API Calls
AI-generated code often fetches data in loops:
// BAD — N+1 queries
const users = await db.query("SELECT * FROM users");
for (const user of users) {
user.posts = await db.query("SELECT * FROM posts WHERE user_id = ?", [user.id]);
}
// GOOD — single query with join
const users = await db.query(`
SELECT u.*, json_agg(p.*) as posts
FROM users u
LEFT JOIN posts p ON p.user_id = u.id
GROUP BY u.id
`);
5. Unoptimized Images
AI generates <img> tags. Next.js has a purpose-built Image component:
// AI generates
<img src="/hero.jpg" alt="Hero" />
// Better
import Image from "next/image";
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />
The Next.js Image component automatically:
- Serves WebP/AVIF formats
- Lazy loads images below the fold
- Prevents layout shift with width/height
- Serves responsive sizes
Measuring Performance
Core Web Vitals
The metrics that matter:
- LCP (Largest Contentful Paint): < 2.5s — how fast the main content loads
- FID (First Input Delay): < 100ms — how fast the page responds to interaction
- CLS (Cumulative Layout Shift): < 0.1 — how much the layout jumps around
Tools
- Lighthouse (built into Chrome DevTools) — run it on every page
- React DevTools Profiler — shows which components re-render and why
- next build output — shows bundle sizes per page
Quick Wins
If you want to improve performance right now:
- Add
loading="lazy"to images below the fold - Add
priorityto your hero image / LCP element - Move client-side data fetching to server components where possible
- Replace
useEffect+fetchwith server-side data fetching in Next.js - Add
Suspenseboundaries around slow-loading components
The Performance Checklist
Before deploying, check:
- [ ] Lighthouse score > 90 on mobile
- [ ] First Load JS < 100KB per page
- [ ] No unnecessary
"use client"directives - [ ] Images use Next.js Image component
- [ ] Heavy libraries are dynamically imported
- [ ] API calls are batched (no N+1)
- [ ] Lists have proper
keyprops - [ ] Expensive computations are memoized
AI can generate fast code — but only if you prompt for it and verify the results.
Stay in the flow
Get vibecoding tips, new tool announcements, and guides delivered to your inbox.
No spam, unsubscribe anytime.