Technical Guide

Web App Bloats to 10MB: AI Frontend Payload Overkill

Unused components ballooned the bundle; we tree-shook & split code, slicing JS size 65%.

January 15, 2025 6 min read

The problem

A Vue.js web app was shipping 10.4MB of JavaScript to users, causing 18-second load times on average connections. The login page alone downloaded 127 component files, including a full WYSIWYG editor, 3D graphics library, and PDF generator - none of which were used on that page. Mobile users couldn't even load the site, with 89% bouncing before the page rendered. The hosting bill hit $12,000/month from bandwidth alone.

How AI created this issue

Developers had built the entire app by asking ChatGPT for Vue component examples. ChatGPT's approach was to import everything globally:


// ChatGPT's main.js - imports everything everywhere
import { createApp } from 'vue'
import App from './App.vue'

// ChatGPT imported entire UI libraries
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' // 500KB CSS
import Vuetify from 'vuetify'
import 'vuetify/styles' // Another 300KB CSS
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css' // 400KB more CSS

// Heavy libraries imported globally
import * as THREE from 'three' // 1MB - for one 3D logo animation
import Quill from 'quill' // 400KB - used on 1 admin page
import pdfMake from 'pdfmake/build/pdfmake' // 2MB
import pdfFonts from 'pdfmake/build/vfs_fonts' // 3MB
import Chart from 'chart.js/auto' // 600KB
import anime from 'animejs' // 200KB
import Swiper from 'swiper' // 300KB

// Register EVERYTHING globally
const app = createApp(App)
app.use(ElementPlus)
app.use(Vuetify)
app.use(Antd)

// Import all 200+ custom components
import './components' // This imported every component

// ChatGPT's component auto-registration
const components = import.meta.globEager('./components/**/*.vue')
for (const path in components) {
  const name = path.split('/').pop().replace(/\.\w+$/, '')
  app.component(name, components[path].default)
}

app.mount('#app')

ChatGPT treated the Vue app like a kitchen sink - importing three different UI frameworks, registering every component globally, and loading massive libraries for features used on single pages. The AI never mentioned lazy loading, tree shaking, or bundle analysis.

The solution

  1. Route-based code splitting with dynamic imports:
    
    // Optimized router with lazy loading
    const router = createRouter({
      routes: [
        {
          path: '/',
          component: () => import('./views/Home.vue')
        },
        {
          path: '/editor',
          component: () => import('./views/Editor.vue'),
          // Load heavy deps only when needed
          beforeEnter: async () => {
            await import('quill/dist/quill.snow.css')
          }
        },
        {
          path: '/reports',
          component: () => import('./views/Reports.vue')
        }
      ]
    })
    
    // Components loaded only where used
    // Editor.vue
    export default {
      components: {
        QuillEditor: () => import('./QuillEditor.vue')
      },
      async mounted() {
        // Dynamic import when component mounts
        const { default: Quill } = await import('quill')
        this.editor = new Quill(this.$refs.editor)
      }
    }
  2. Component library optimization: Use only what's needed:
    
    // main.js - minimal imports
    import { createApp } from 'vue'
    import App from './App.vue'
    
    // Import only used Element Plus components
    import {
      ElButton,
      ElInput,
      ElForm,
      ElMessage
    } from 'element-plus'
    
    const app = createApp(App)
    
    // Register only what we use
    app.use(ElButton)
    app.use(ElInput)
    app.use(ElForm)
    app.config.globalProperties.$message = ElMessage
    
    // No global component registration
    app.mount('#app')
  3. Vite configuration for optimal bundling:
    
    // vite.config.js
    export default {
      build: {
        rollupOptions: {
          output: {
            manualChunks: {
              'vendor': ['vue', 'vue-router'],
              'ui': ['element-plus'],
            }
          }
        },
        // Analyze bundle
        analyze: true
      },
      // Auto-import components as needed
      plugins: [
        Components({
          dirs: ['src/components'],
          deep: true,
          // Only import when used in templates
          importPathTransform: v => v
        })
      ]
    }
  4. Progressive enhancement: Load features as users need them
  5. Image optimization: Lazy loading and modern formats

The results

  • Bundle size: 10.4MB → 1.3MB (87% reduction)
  • Initial load: 680KB (vs 10.4MB before)
  • Time to Interactive: 18s → 2.1s on 3G
  • Lighthouse score: 19 → 96
  • Bandwidth costs: $12,000 → $1,800/month
  • Mobile bounce rate: 89% → 22%

The team learned that AI examples optimize for simplicity, not performance. Every import has a cost. They now start with minimal imports and add features progressively. Bundle size is reviewed in every PR, and they maintain a "performance budget" of 1MB for initial load.

Ready to fix your codebase?

Let us analyze your application and resolve these issues before they impact your users.

Get Diagnostic Assessment →