AI-Optimized Asset Bundling: How I Cut Frontend Load Time in Half

I wired AI into my bundling pipeline to handle minification, dead-code hunting and asset grouping. It cut load time in half without a framework rewrite.
AI-Optimized Asset Bundling: How I Cut Frontend Load Time in Half
Photo by Nubelson Fernandes / Unsplash

Why I Let AI Touch My Bundles

I like fast sites. I also like shipping features without spending my weekend rewriting the build config from scratch for the fifteenth time.

On a recent client project the frontend felt sluggish on 3G. Lighthouse was whining. CLS, TTFB, all the usual suspects. The audits were not horrible, but the bundle shape clearly was.

Classic fix would be: refactor imports, clean dead code, tweak Webpack or Vite, hand tune split points, and hope you did not miss something in a random dynamic import. Boring. Repetitive. Easy to screw up.

So I tried something else. I used AI to inspect, propose, and in some cases directly rewrite how my assets are bundled and minified. Not by replacing the bundler. By putting a smart assistant on top of it.

The result: around 52% smaller initial JS payload on the worst page, cleaner CSS, and fewer roundtrips for fonts and icons. I did not touch the framework. I barely touched the code. I mostly wired AI around the build output.

The Starting Point: Messy But Familiar

Stack was pretty standard:

  • React with Vite
  • Tailwind plus a few legacy CSS files
  • Some old jQuery widgets that refused to die
  • Icon fonts, a couple of webfonts, random SVGs in /public

Vite was already doing minification and code splitting. On paper that looked fine. In reality the network panel showed the problem.

  • Huge vendor.js bundle doing way too much work
  • CSS with entire components that did not exist anymore
  • Multiple icon strategies fighting each other
  • Some code only needed in admin flows loaded on the public homepage

Nothing new if you build production apps for a while. Tech debt collects in the bundle. Traditional clean up is manual and slow. Perfect spot to throw an LLM at it.

My Basic Strategy: AI As a Bundle Analyst

I did not ask AI to magically produce a perfect config file. That usually ends with hallucinated plugin names and made-up flags.

Instead I treated it like a curious junior dev who reads everything and then suggests refactors. Only this junior does not get bored scrolling through 2 MB of source map output.

The workflow settled into three steps:

  1. Generate detailed bundle stats and raw assets.
  2. Feed structured snapshots into an LLM with strict prompts.
  3. Automate the low-risk changes. Manually confirm the scary ones.

Nothing fancy. Just ruthless feedback loops.

Step 1: Surfacing The Real Bundle Shape

First I turned my bundle into something a machine can reason about.

Vite has a --mode production build with rollup under the hood. I added a stats plugin so I could dump chunk information to JSON. For non-Rollup projects you can do the same with Webpack stats, esbuild metafiles or even a quick script that crawls your dist folder.

The important part is structure. I wanted for each chunk:

  • Chunk name and size (gzip and brotli if possible)
  • List of modules with their individual contribution
  • Dynamic import boundaries
  • Which entry points hit which chunks

I also grabbed:

  • The biggest CSS files
  • The HTML shell for the key routes
  • A HAR file from a throttled network run on mobile

Yes, it is a lot of data. That is the point. Humans skim. AI does not care. It just wants token limits.

Step 2: Constraining The AI So It Does Not Hallucinate Tools

Naive prompt is something like “optimize my bundle.” That gives you generic blog-post answers.

I went much more specific. I told the model exactly what is off-limits and what is allowed. Something like this:

System: You are working inside a Vite + React project.
- You may NOT introduce new libraries.
- You may NOT change frameworks.
- You MAY suggest: import changes, dynamic imports, 
  chunking strategies, removing unused CSS or JS.
- Prefer concrete file paths and diff-style answers.

User: Here is the bundle stats JSON for the production build.
Focus on:
1) JS bundles > 100kb
2) CSS files > 50kb
3) Assets requested during initial page load
Only output:
- A list of concrete refactor suggestions
- For each, estimate saved KB and risk level (low/medium/high)

I pasted chunks of stats and assets, not the whole repo. Then I iterated.

This produced surprisingly usable ideas. Examples:

  • node_modules/date-fns/locale/* is mostly unused, consider trimming locales.”
  • src/components/admin/ appears in the main bundle, but the route is never hit on public pages.”
  • “These three Tailwind utility classes compile into identical CSS rules.”

The model is very good at pattern spotting across files that my eyes would glaze over.

Step 3: Letting AI Rewrite The Safe Stuff

Once I had a list of candidates I split them in two buckets.

  • Safe automation: changes that are easy to test and revert, like trimming locales, removing unused imports, splitting routes.
  • Manual review: anything around auth, legacy widgets, or gnarly CSS relying on cascade magic.

For the safe bucket I actually let AI write code changes. I fed it the relevant files plus the stats context and asked for specific patches.

Example prompt for dynamic imports:

System: You are editing a React + Vite SPA.
User: This chunk is too big and used only on route /reports.
Here is the current router file and the component.
Refactor to lazy-load the ReportsPage so it loads only when needed.
Return a unified diff. Nothing else.

The output was usually a decent React.lazy refactor with Suspense boundaries. I pasted that into a branch, ran tests, and checked the new bundle graph. If it passed, it shipped.

Same idea for dead imports. I asked it to:

  • Identify imports that are not referenced in the file.
  • Remove them and update types if needed.
  • Show before/after at the top of the file.

You can script this via the API, but I kept it semi-manual for this project. Copy, patch, run tests, repeat.

Minification: AI As a CSS and SVG Surgeon

Minifying JS is solved. Terser, esbuild, swc, all solid. Where AI helped more was messy CSS and SVG.

I had a legacy styles.css that was 140kb by itself. Tailwind was on top, but old selectors still existed. There were also SVGs exported from Figma that looked like someone dumped their soul into the path data.

For CSS, I took a two-phase approach.

  1. Run PurgeCSS / Tailwind JIT for obvious dead rules.
  2. Then let the AI reason about what was left.

I extracted only the remaining large rule blocks and the HTML fragments that used them. Then asked:

User: Here is a CSS file and the HTML snippets that reference it.
Goal:
- Reduce file size.
- Avoid visual regressions.
You may:
- Merge duplicate rules.
- Shorten selectors when safe.
- Remove vendor prefixes for modern browsers.
Show the new CSS and explain each removal in one line.

The model merged a lot of duplicated button styles, removed leftover -webkit- noise, and caught a few no-longer-used utility classes that PurgeCSS missed because they were referenced in a dead JS path.

For SVGs I literally pasted them and said:

User: Here is an SVG icon used at 24x24.
Losslessly minify it.
You may:
- Simplify path commands.
- Remove metadata and invisible attributes.
- Replace inline styles with attributes.

File sizes dropped by 20–60% depending on how bad the original export was. Could I have used SVGO? Absolutely. But the AI explained why each attribute was safe to drop, which helped when cleaning up more complex illustrations.

AI As A Bundle Split Designer

One unexpected use: planning how to split bundles so that routes share the right code without crazy duplication.

Most code splitting guides tell you to group “routes that share dependencies.” That is vague in a real codebase. I gave the LLM a map.

First I generated a list of routes with their imported modules, straight from the router file and some static analysis. Then I asked:

User: Here are all routes and the modules they import.
Design a chunking strategy with goals:
- Minimize initial load for routes /, /pricing, /about.
- Do not create more than 8 chunks total.
- Avoid loading admin-only code on public pages.
Output:
- A bullet list of chunks you would create.
- For each, which routes would load it.
- Any needed dynamic imports or shared utilities.

The answer was basically a human-readable chunking plan. It grouped marketing pages together, put admin and reports into late-loaded chunks, and pulled shared form components into a separate utility chunk.

I did not copy it blindly. But it gave me a clear target to implement in Vite’s Rollup config. One pass versus me playing whack-a-mole with manualChunks for hours.

Automation: Wiring This Into CI Without Burning It All Down

One-off cleanups are nice. I wanted something that would keep the bundle honest over time.

I built a small script that runs in CI on main:

  1. Run the production build.
  2. Generate bundle stats JSON.
  3. Compare to the last successful build.
  4. If any chunk grows more than 30%, send a diff to an LLM.
  5. Post its summary into a Slack channel.

The LLM gets a prompt like:

User: Here are the old and new stats.
Explain:
- Which new modules caused the size increase.
- Whether they look essential for the feature (based on their names).
- 2 suggestions to reduce the overhead.
Keep it under 200 words.

This does not auto-fix anything. It just makes weight gain visible immediately. Devs see “Hey, adding that one datepicker brought in all of Moment with locales” instead of discovering it three sprints later.

When the warning looks trivial to fix, I sometimes let a GitHub Action call an LLM to suggest a patch and open a PR. Only for things like removing unused imports or swapping a whole-locale import for a narrow one. Humans still review.

What Worked, What Did Not

This was not magic. It was messy and occasionally annoying. But it moved the numbers.

Wins:

  • Initial JS payload on the main marketing page went from ~320kb gzipped to ~155kb.
  • CSS dropped by about 35kb after cleaning legacy styles.
  • Route transitions felt snappier once reports and admin moved out of the main bundle.
  • Time spent staring at bundle visualizers went to almost zero.

Pain points:

  • Token limits. You cannot just paste your whole stats file into the chat UI for big apps. I had to script chunked prompts.
  • Hallucinated tooling. If I did not explicitly say “no new libraries,” the model loved to suggest imaginary Rollup plugins.
  • Risk around CSS. One “obvious” deduplication broke a very old IE11 fallback hack. Thankfully we do not care about IE11 anymore, but you get the idea.

Still worth it. I spent more time deciding tradeoffs and less time manually hunting for 15kb wins.

Where I Think AI Fits In Frontend Performance Work

I do not want AI picking my framework. I do not want it architecting the entire app. That feels irresponsible.

I do want it handling the boring, high-scope analysis jobs. Bundle size diagnostics. Dead code hunting. Protocol-level network traces. Huge CSS cleanups.

These tasks are pattern recognition at scale. Humans are bad at that beyond a certain point. LLMs are quite good, as long as you box them in and keep them near the code you already trust.

If you are sitting on a bloated frontend and you are not ready for a full rewrite, I would absolutely bolt an LLM onto your build pipeline. Start small.

  • Generate bundle stats.
  • Feed just the worst chunks into an LLM with a strict prompt.
  • Let it propose refactors and only auto-apply the obvious ones.

It will not replace performance work. It will make the grunt work cheaper. That is enough to matter.

I am not going back to purely manual bundle archaeology after this. There is no point. I would rather ship features and let the AI complain when my bundles get fat again.

Subscribe to my newsletter

Subscribe to my newsletter to get the latest updates and news

Member discussion