vite-bundle-optimization
Teaches Vite-specific bundle optimization patterns. Use when configuring Vite builds, code splitting, managing dependencies, or troubleshooting slow Vite builds.
What this skill does
# Vite Bundle Optimization
## Table of Contents
- [When to Use](#when-to-use)
- [Instructions](#instructions)
- [Details](#details)
- [Source](#source)
Production-ready patterns for optimizing bundle size and build performance in Vite + React applications. These patterns leverage Vite's architecture (native ESM in dev, Rollup in production) to deliver smaller, faster bundles.
## When to Use
Reference these patterns when:
- Setting up a new Vite + React project for production
- Analyzing bundle size with `npx vite-bundle-visualizer`
- Build times are slow or bundles are unexpectedly large
- Migrating from webpack/CRA to Vite
- Optimizing Core Web Vitals (LCP, FID/INP, CLS)
## Instructions
- Apply these patterns during project setup, build configuration, and bundle size reviews. When you see large bundles or slow builds, diagnose with `npx vite-bundle-visualizer` and apply the relevant pattern.
## Details
### Overview
Vite uses esbuild for dependency pre-bundling and development transforms, and Rollup for production builds. Understanding this dual architecture is key to optimizing effectively. The patterns below are ordered by impact.
---
### 1. Avoid Barrel File Imports
**Impact: CRITICAL** — Can add 200-800ms to startup and 2-4s to dev server boot.
Barrel files (`index.ts` that re-export from many modules) force bundlers to load the entire module graph even when you only use one export. This is the #1 bundle size issue in React apps.
**Avoid — imports entire library through barrel:**
```tsx
import { Button, TextField } from '@/components'
// Loads ALL components in the barrel, even unused ones
import { Check, X, Menu } from 'lucide-react'
// Loads all 1,500+ icons (~2.8s in dev)
```
**Prefer — direct imports:**
```tsx
import { Button } from '@/components/Button'
import { TextField } from '@/components/TextField'
import Check from 'lucide-react/dist/esm/icons/check'
import X from 'lucide-react/dist/esm/icons/x'
import Menu from 'lucide-react/dist/esm/icons/menu'
```
**Auto-fix with `vite-plugin-barrel`:**
```typescript
// vite.config.ts
import barrel from 'vite-plugin-barrel'
export default defineConfig({
plugins: [
react(),
barrel({
packages: ['lucide-react', '@mui/material', '@mui/icons-material'],
}),
],
})
```
This transforms barrel imports into direct imports at build time, giving you ergonomic syntax with direct-import performance.
**Commonly affected libraries:** `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@radix-ui/react-*`, `lodash`, `date-fns`, `rxjs`.
---
### 2. Configure Manual Chunk Splitting
**Impact: HIGH** — Better caching, parallel loading, smaller initial bundle.
Vite's default chunking puts all vendor code into one file. Split it so that frequently-changing app code doesn't invalidate the vendor cache.
```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
manualChunks: {
// Core React — rarely changes
'vendor-react': ['react', 'react-dom'],
// Router — changes infrequently
'vendor-router': ['react-router-dom'],
// Data layer — changes occasionally
'vendor-query': ['@tanstack/react-query'],
// UI framework — changes with design updates
'vendor-ui': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
},
},
},
},
})
```
For more dynamic splitting based on module paths:
```typescript
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('react-dom')) return 'vendor-react'
if (id.includes('react-router')) return 'vendor-router'
if (id.includes('@tanstack')) return 'vendor-query'
return 'vendor' // everything else
}
},
```
---
### 3. Dynamic Imports for Route-Level Code Splitting
**Impact: HIGH** — Load only the code needed for the current page.
Use `React.lazy()` with dynamic imports to split each route into its own chunk.
```tsx
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
const Home = lazy(() => import('./pages/Home'))
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
function App() {
return (
<BrowserRouter>
<Suspense fallback={<PageSkeleton />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</BrowserRouter>
)
}
```
Vite automatically creates separate chunks for each lazy import. Name them for easier debugging:
```tsx
const Dashboard = lazy(() =>
import(/* webpackChunkName: "dashboard" */ './pages/Dashboard')
)
```
---
### 4. Lazy-Load Heavy Components Below the Fold
**Impact: HIGH** — Reduces initial bundle for faster LCP.
Components that aren't visible on initial load (modals, charts, editors, maps) should be lazy-loaded.
```tsx
import { lazy, Suspense, useState } from 'react'
const RichTextEditor = lazy(() => import('./components/RichTextEditor'))
const ChartPanel = lazy(() => import('./components/ChartPanel'))
function ArticlePage() {
const [editing, setEditing] = useState(false)
return (
<article>
<h1>Article Title</h1>
<p>Content visible immediately...</p>
{editing && (
<Suspense fallback={<EditorSkeleton />}>
<RichTextEditor />
</Suspense>
)}
<Suspense fallback={<ChartSkeleton />}>
<ChartPanel />
</Suspense>
</article>
)
}
```
---
### 5. Defer Third-Party Scripts
**Impact: HIGH** — Analytics, tracking, and widgets shouldn't block rendering.
Load non-critical third-party scripts after the page is interactive.
**Avoid — blocks initial render:**
```tsx
// main.tsx
import * as Sentry from '@sentry/react'
import posthog from 'posthog-js'
Sentry.init({ dsn: '...' })
posthog.init('...')
```
**Prefer — load after hydration/mount:**
```tsx
// main.tsx — defer to idle time
function initThirdParty() {
import('@sentry/react').then(Sentry => {
Sentry.init({ dsn: import.meta.env.VITE_SENTRY_DSN })
})
import('posthog-js').then(({ default: posthog }) => {
posthog.init(import.meta.env.VITE_POSTHOG_KEY)
})
}
if ('requestIdleCallback' in window) {
requestIdleCallback(initThirdParty)
} else {
setTimeout(initThirdParty, 2000)
}
```
For external script tags, use `defer` or dynamically inject them:
```typescript
function loadScript(src: string) {
const script = document.createElement('script')
script.src = src
script.async = true
document.body.appendChild(script)
}
```
---
### 6. Preload Critical Assets on User Intent
**Impact: MEDIUM** — Eliminates perceived latency on navigation.
Start loading a route's code when the user signals intent (hover, focus) rather than waiting for the click.
```tsx
function NavLink({ to, children }: { to: string; children: React.ReactNode }) {
const preload = () => {
// Vite creates a module preload for dynamic imports
switch (to) {
case '/dashboard':
import('./pages/Dashboard')
break
case '/settings':
import('./pages/Settings')
break
}
}
return (
<Link to={to} onMouseEnter={preload} onFocus={preload}>
{children}
</Link>
)
}
```
For `<link rel="modulepreload">` in the HTML head:
```html
<!-- Preload critical route chunks -->
<link rel="modulepreload" href="/assets/Home-abc123.js" />
```
Vite automatically adds `<link rel="modulepreload">` for entry chunks. Add manual preloads for routes you know users will visit next.
---
### 7. Configure Dependency Pre-Bundling
**Impact: MEDIUM** — Faster dev server startup and page loads.
Vite pre-bundles `node_modules` dependencies uRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.