Quick Answer
Vibe-coded apps can ship far more JavaScript on initial load than they need; for comparison, the median mobile home page in HTTP Archive's 2024 Web Almanac loads 558 KB of it. AI tools import entire libraries instead of individual functions, include unused dependencies, skip image optimization, and generate inline styles instead of utility classes. The result is an app that is slow to become interactive on mobile. Fix it by auditing your bundle, removing unused imports, lazy-loading heavy components, and compressing images.
The Page Weight Problem in AI-Generated Apps
AI code generators optimize for functionality, not performance. When you ask for a chart, they import all of Chart.js. When you ask for a date picker, they import all of Moment.js. When you ask for an icon, they import the entire icon library. Each of these choices can add tens or hundreds of kilobytes to your bundle.
On mobile connections, every extra megabyte adds load time, and slow pages lose visitors. The HTTP Archive's 2024 Web Almanac puts the median mobile page at 2,311 KB, and AI-generated apps with unoptimized dependency trees can weigh far more.
Every extra kilobyte of JavaScript also has to be parsed and run, and that costs the most on mid-range phones. Google's Core Web Vitals guidance asks for Largest Contentful Paint (LCP) within 2.5 seconds.
Where the Weight Comes From
| Source | Typical Bloat | AI Tool Habit | Right Approach |
|---|---|---|---|
| Full library imports | About 72KB minified for all of lodash | import _ from 'lodash' |
import get from 'lodash/get' |
| Moment.js | About 300KB minified (with locales) | Imported for simple date formatting | Use date-fns (tree-shakeable: you ship only the functions you import) |
| Icon libraries | The whole icon set | import * as Icons from 'react-icons/fa', then Icons[name] |
Import individual icons only |
| Unoptimized images | Can be megabytes per image | Full-resolution PNGs in hero sections | WebP/AVIF, responsive srcset, lazy loading |
| CSS frameworks | About 230KB for Bootstrap 5's minified CSS | Full Bootstrap imported for a few components | Tailwind (generates only the classes you use), or CSS modules |
| Unused dependencies | Whatever they add to the bundle | Dependencies from abandoned features | Find and remove them with knip |
How to Audit Your Page Weight
Before fixing anything, measure what you are shipping. Three tools give you the full picture:
Lighthouse Performance Audit. Open Chrome DevTools, go to Lighthouse, and run a Performance audit on mobile. Look at Total Blocking Time (TBT), Largest Contentful Paint (LCP), and the "Reduce unused JavaScript" audit. Lighthouse tells you how much JavaScript is loaded but never executed.
Bundle Analyzer. For Next.js 16.1 and later on Turbopack, run npx next analyze. On webpack builds, add @next/bundle-analyzer to your config and run ANALYZE=true npm run build. For Vite apps, use rollup-plugin-visualizer. These tools visualize every dependency in your bundle by size, making it obvious which imports are consuming the most space.
Network Tab. Chrome DevTools Network tab with "Disable cache" checked and throttling set to "3G" shows you the experience on a slow connection. Sort by size to find the largest resources.
Fixing the Most Common Issues
// ❌ BAD - Full library import (pulls in all of lodash, about 72KB minified)
import _ from 'lodash';
const value = _.get(data, 'user.name');
// ✅ GOOD - Per-function import (about 9KB minified)
import get from 'lodash/get';
const value = get(data, 'user.name');
// ❌ BAD - Moment.js for simple formatting (about 300KB minified)
import moment from 'moment';
const formatted = moment(date).format('MMM D, YYYY');
// ✅ GOOD - date-fns ships only the functions you import
import { format } from 'date-fns';
const formatted = format(date, 'MMM d, yyyy');
// ❌ BAD - Loading heavy component on initial render
import HeavyChart from './HeavyChart';
// ✅ GOOD - Lazy load heavy components into their own chunk
import { lazy, Suspense } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<div>Loading chart...</div>}>
<HeavyChart />
</Suspense>
);
}
Setting a Page Weight Budget
A performance budget prevents regressions after you fix the initial bloat. Set hard limits that block deployment if exceeded:
| Resource Type | Budget (Compressed) | Why This Limit |
|---|---|---|
| Total JavaScript | < 300KB | Parse and run cost on mid-range phones |
| Total CSS | < 50KB | Render-blocking, affects FCP |
| Total Images (above fold) | < 200KB | Affects LCP directly |
| Total Page Weight | < 1MB | Download time on slow mobile networks |
| Third-party Scripts | < 100KB | Analytics, chat widgets, ad scripts |
Tools like VibeDoctor's Vibe Check automatically measure your deployed site's page weight and request count, and flag full lodash and Moment.js imports in your codebase. Free to sign up.
FAQ
What is a good page weight for a web app?
Under 1MB compressed total for the initial load. JavaScript should be under 300KB compressed. For landing pages that need to rank well, aim for under 500KB total. The lighter the page, the better your Core Web Vitals scores and the lower your bounce rate.
Does tree shaking fix the problem automatically?
Only partially. Tree shaking removes unused exports from ES modules, but some libraries (Moment.js, the default lodash import) are not tree-shakeable because they ship as CommonJS or as one big object. It also fails when code looks exports up by name at runtime, such as Icons[name] on a whole icon set. You need to use tree-shakeable alternatives or import from specific subpaths.
How much does page weight affect SEO?
Google says good Core Web Vitals (LCP, CLS, INP), along with other page experience aspects, align with what its core ranking systems seek to reward. Page weight directly affects LCP - heavier pages take longer to render the largest content element.
Should I use a CDN to fix page weight issues?
A CDN reduces latency (download time) but does not reduce page weight (total bytes). If you are shipping 5MB of JavaScript, a CDN makes it download faster but the browser still has to parse and execute all 5MB. Fix the weight first, then add a CDN for latency. Both matter, but weight is the bigger problem.