LearnThatStack Ace your next interview

Vite Frontend Development.
Cheat sheet.

Quick reference for Vite Frontend Development - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Frontend Development 18-section reference ~8 min read

Summary

Vite is a next-generation frontend build tool created by Evan You (Vue.js creator) that revolutionizes the development experience through its no-bundle development server and lightning-fast Hot Module Replacement (HMR). It leverages native ES modules in the browser during development, eliminating the need for bundling, which results in instant server starts regardless of application size. Vite pre-bundles dependencies using esbuild (written in Go) making it 10-100x faster than JavaScript-based bundlers. For production, it uses Rollup to produce highly optimized static assets with automatic code-splitting and tree-shaking. With first-class TypeScript support, framework-agnostic architecture, and a rich plugin ecosystem, Vite has become the preferred choice for modern web development. This cheatsheet covers Vite fundamentals, configuration, optimization strategies, and best practices for technical interviews.

1. What is Vite?

Vite (French for "quick") is a modern frontend build tool that provides:

  • Lightning-fast HMR (Hot Module Replacement)
  • Native ES modules during development
  • Pre-configured Rollup for production builds
  • Framework agnostic with first-class support for Vue, React, Preact, etc.

Key Differentiators:

  • No bundling in development (uses native ESM)
  • Instant server start regardless of app size
  • Pre-bundling dependencies with esbuild (10-100x faster than JS-based bundlers)

2. Installation & Project Setup

Create New Project

# With npm
npm create vite@latest my-app -- --template react

# With yarn
yarn create vite my-app --template react

# With pnpm
pnpm create vite my-app -- --template react

# Available templates:
# vanilla, vanilla-ts, vue, vue-ts, react, react-ts, 
# preact, preact-ts, lit, lit-ts, svelte, svelte-ts

Add to Existing Project

npm install -D vite

Basic package.json Scripts

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

3. Project Structure

my-vite-app/
├── index.html          # Entry point (not in public/)
├── package.json
├── vite.config.js      # Vite configuration
├── public/             # Static assets (copied as-is)
│   └── favicon.ico
└── src/
    ├── main.js         # JavaScript entry
    ├── App.jsx
    └── style.css

Important: index.html is the entry point and must include:

<script type="module" src="/src/main.js"></script>

4. Configuration (vite.config.js)

Basic Configuration

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    open: true
  },
  build: {
    outDir: 'dist',
    sourcemap: true
  }
})

Environment-based Configuration

export default defineConfig(({ command, mode }) => {
  if (command === 'serve') {
    return {
      // dev specific config
    }
  } else {
    return {
      // build specific config
    }
  }
})

Common Configuration Options

export default defineConfig({
  // Project root
  root: process.cwd(),
  
  // Base public path
  base: '/my-app/',
  
  // Directory to serve as static
  publicDir: 'public',
  
  // Dependency optimization
  optimizeDeps: {
    include: ['lodash'],
    exclude: ['@vueuse/core']
  },
  
  // Build options
  build: {
    target: 'es2015',
    minify: 'terser',
    cssCodeSplit: true,
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        nested: resolve(__dirname, 'nested/index.html')
      }
    }
  }
})

5. Development Server

Server Options

server: {
  host: '0.0.0.0',     // Listen on all addresses
  port: 3000,
  strictPort: true,    // Exit if port is in use
  https: true,         // Enable HTTPS
  open: '/docs',       // Open browser to specific route
  proxy: {
    '/api': {
      target: 'http://localhost:5000',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api/, '')
    }
  }
}

HMR (Hot Module Replacement)

// Accept updates for this module
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // Handle the updated module
  })
  
  // Clean up side effects
  import.meta.hot.dispose(() => {
    // cleanup code
  })
}

6. Asset Handling

Importing Assets

// Import as URL
import imgUrl from './img.png'

// Import as string (with ?raw)
import shaderString from './shader.glsl?raw'

// Import as Worker
import Worker from './worker.js?worker'

// Import CSS as string
import style from './style.css?inline'

Public Directory

<!-- Reference public assets with absolute path -->
<img src="/logo.png" alt="Logo">

Dynamic Imports

const module = await import(`./locales/${locale}.js`)

// With Glob Import
const modules = import.meta.glob('./modules/*.js')
// Eager loading
const modules = import.meta.glob('./modules/*.js', { eager: true })

7. CSS Handling

CSS Modules

// style.module.css
import styles from './style.module.css'
<div className={styles.container}>

PostCSS

// postcss.config.js
export default {
  plugins: {
    autoprefixer: {},
    'postcss-nested': {}
  }
}

Preprocessors

# Install preprocessor
npm install -D sass

# Just use it
import './style.scss'

CSS-in-JS

// Works out of the box
import styled from 'styled-components'
const Button = styled.button`
  color: blue;
`

8. TypeScript Support

Setup

# No configuration needed, just use .ts files
npm install -D typescript

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "node",
    "jsx": "preserve",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": ["vite/client"]
  }
}

Type Declarations

// vite-env.d.ts
/// <reference types="vite/client" />

// For asset imports
declare module '*.svg' {
  const content: string
  export default content
}

9. Environment Variables

Define Variables

# .env
VITE_API_URL=https://api.example.com

# .env.local (ignored by git)
VITE_SECRET_KEY=secret123

Access in Code

// Only VITE_ prefixed variables are exposed
console.log(import.meta.env.VITE_API_URL)

// Built-in variables
import.meta.env.MODE      // 'development' or 'production'
import.meta.env.BASE_URL  // Base URL from config
import.meta.env.PROD      // boolean
import.meta.env.DEV       // boolean

TypeScript Support

// vite-env.d.ts
interface ImportMetaEnv {
  readonly VITE_API_URL: string
}

10. Plugins

Essential Plugins

// React
import react from '@vitejs/plugin-react'

// Vue
import vue from '@vitejs/plugin-vue'

// Auto-import APIs
import AutoImport from 'unplugin-auto-import/vite'

// Auto-import components
import Components from 'unplugin-vue-components/vite'

Writing Custom Plugin

function myPlugin() {
  return {
    name: 'my-plugin',
    
    // Called on config resolution
    config(config, { command }) {
      if (command === 'build') {
        config.base = '/dist/'
      }
    },
    
    // Transform code
    transform(code, id) {
      if (id.endsWith('.custom')) {
        return {
          code: transformCustom(code),
          map: null
        }
      }
    },
    
    // Dev server specific
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        // custom middleware
        next()
      })
    }
  }
}

11. Build Optimization

Code Splitting

// Dynamic imports create separate chunks
const Home = () => import('./Home.vue')

// Manual chunks
build: {
  rollupOptions: {
    output: {
      manualChunks: {
        vendor: ['react', 'react-dom'],
        utils: ['lodash', 'moment']
      }
    }
  }
}

Tree Shaking

// Automatically enabled for ES modules
import { debounce } from 'lodash-es' // ✅ Tree-shakeable
import _ from 'lodash'               // ❌ Not tree-shakeable

Build Analysis

# Install analyzer
npm install -D rollup-plugin-visualizer

# Configure
import { visualizer } from 'rollup-plugin-visualizer'
plugins: [visualizer()]

12. Performance Features

Dependency Pre-Bundling

// Force include/exclude
optimizeDeps: {
  include: ['linked-dep'],
  exclude: ['large-dep']
}

Lazy Loading Routes

// React Router
const About = lazy(() => import('./About'))

// Vue Router
{
  path: '/about',
  component: () => import('./About.vue')
}

Web Workers

// Import as worker
import MyWorker from './worker?worker'
const worker = new MyWorker()

13. Production Build

Build Command Options

vite build --base=/my-app/ --mode staging --sourcemap

Preview Production Build

vite preview --port 8080

Static Site Generation (SSG)

// vite.config.js for SSG
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        main: 'index.html',
        about: 'about.html'
      }
    }
  }
})

14. Common Patterns

Alias Configuration

resolve: {
  alias: {
    '@': '/src',
    '@components': '/src/components',
    '@utils': '/src/utils'
  }
}

Global Variables

define: {
  __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}

Library Mode

build: {
  lib: {
    entry: resolve(__dirname, 'src/index.js'),
    name: 'MyLib',
    fileName: 'my-lib'
  },
  rollupOptions: {
    external: ['vue'],
    output: {
      globals: {
        vue: 'Vue'
      }
    }
  }
}

15. Advanced Patterns & Debugging

Server-Side Rendering (SSR)

// vite.config.js
export default defineConfig({
  ssr: {
    noExternal: ['vue-router'], // Dependencies to bundle for SSR
    target: 'node',
    format: 'cjs'
  }
})

// server.js
import { createServer } from 'vite'
const vite = await createServer({
  server: { middlewareMode: true },
  appType: 'custom'
})
app.use(vite.middlewares)

Monorepo Setup

// Root vite.config.js
export default defineConfig({
  resolve: {
    alias: {
      '@shared': path.resolve(__dirname, '../shared/src'),
      '@components': path.resolve(__dirname, '../components/src')
    }
  },
  optimizeDeps: {
    include: ['@myorg/shared', '@myorg/components']
  }
})

Debug Strategies

# Verbose logging
vite --debug

# Clear cache
rm -rf node_modules/.vite

# Profile build
vite build --profile

Common Issues & Solutions

Issue Cause Solution
Slow dev server Large dependencies Add to optimizeDeps.include
Module not found Incorrect alias Check resolve.alias config
CORS errors Backend on different port Configure server.proxy
Memory errors Large builds NODE_OPTIONS='--max-old-space-size=4096'
HMR not working Circular dependencies Refactor imports
Build size too large No code splitting Add dynamic imports

16. Key Interview Concepts

Vite vs Other Build Tools

Feature Vite Webpack Parcel Snowpack
Dev Server Start Instant Slow (bundling) Medium Instant
HMR Speed <50ms 200ms-2s 100ms-1s <100ms
Build Tool Rollup Webpack Parcel Rollup/Webpack
Config Complexity Minimal Complex Zero-config Minimal
Bundle Size Optimal Good Good Good
Ecosystem Growing Mature Medium Small
TypeScript Native Plugin Native Native

How Vite's Architecture Works

Development:
1. Browser requests index.html
2. Vite serves HTML with ES module scripts
3. Browser requests JS modules
4. Vite transforms and serves on-demand
5. Dependencies are pre-bundled with esbuild

Production:
1. Rollup bundles all assets
2. Code splitting for dynamic imports
3. CSS code splitting
4. Asset optimization and hashing
5. Legacy browser support via @vitejs/plugin-legacy

Dependency Pre-Bundling Deep Dive

// Why pre-bundling?
// 1. Convert CommonJS/UMD to ESM
const lodash = require('lodash') // CommonJS
// Becomes: import lodash from '/node_modules/.vite/lodash.js'

// 2. Reduce network requests
import { debounce } from 'lodash' // 600+ modules
// Becomes: single pre-bundled file

// 3. Cache for performance
// Stored in node_modules/.vite with strong caching

Performance Optimization Strategies

Strategy Impact Implementation
Code Splitting High Dynamic imports, manual chunks
Tree Shaking High Use ES modules, mark sideEffects
Compression Medium vite-plugin-compression
Image Optimization Medium vite-imagetools
Bundle Analysis Diagnostic rollup-plugin-visualizer
CSS Purging Medium PurgeCSS plugin
Lazy Loading High Route-based splitting

Common Vite Gotchas & Solutions

// Gotcha 1: Glob imports are eager by default
const modules = import.meta.glob('./modules/*.js') // Lazy ✅
const modules = import.meta.glob('./modules/*.js', { eager: true }) // Eager

// Gotcha 2: Environment variables must be prefixed
VITE_API_URL=http://api.com // ✅ Exposed
API_URL=http://api.com // ❌ Not exposed

// Gotcha 3: SSR requires different entry
// Client entry: main.js mounts app
// Server entry: main.js exports app factory

// Gotcha 4: Process is not defined
// Solution: Define in config
define: {
  'process.env': process.env
}

17. Interview Preparation Checklist

  • ✅ Core Concepts: No-bundle dev, native ESM, esbuild pre-bundling
  • ✅ Configuration: vite.config.js, plugins, build options
  • ✅ Asset Handling: Static assets, imports, public directory
  • ✅ CSS: Modules, PostCSS, preprocessors, CSS-in-JS
  • ✅ TypeScript: Zero-config support, type declarations
  • ✅ Environment Variables: VITE_ prefix, import.meta.env
  • ✅ HMR: How it works, accept/dispose patterns
  • ✅ Build Optimization: Code splitting, tree shaking, chunks
  • ✅ SSR/SSG: Server-side rendering setup
  • ✅ Performance: Why Vite is fast, optimization techniques

Key Takeaway: Vite revolutionizes frontend development by leveraging native ES modules for instant dev server starts and lightning-fast HMR. Its combination of esbuild for development and Rollup for production provides the best of both worlds: incredible development experience and optimized production builds. Understanding Vite's no-bundle architecture, dependency pre-bundling, and optimization strategies is crucial for modern web development interviews.

Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime