Skip to content

Vite Dynamic Import — Dynamic Imports with the Vite Bundler

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Vite Dynamic Import. We cover key concepts, practical examples, and best practices to help you master this topic.

Vite handles dynamic imports natively with automatic code splitting and optimized chunk output, requiring minimal configuration for efficient Lazy Loading.

What You'll Learn

By the end of this tutorial, you'll understand how Vite handles dynamic imports, how to configure manual chunks, how to use Vite's built-in optimizations, and how Vite's approach differs from Webpack.

Why It Matters

Vite uses native ES modules for development and Rollup for production builds. Dynamic imports work out of the box with sensible defaults. Understanding Vite's approach helps you configure splitting without Webpack's complexity.

Real-World Use

A Vue 3 application uses Vite with dynamic imports for all routes. Development starts in under 2 seconds. Production builds produce automatic vendor and page chunks totaling 120KB gzipped for the initial page.

Vite Splitting Architecture

graph TD
    A[Vite Build] --> B[Rollup bundler]
    B --> C[Automatic
code splitting] B --> D[Dynamic imports
→ separate chunks] C --> E[Vendor chunk
node_modules] C --> F[Entry chunk
main app code] D --> G[Async chunks
per dynamic import] E --> H[Output files
dist/] F --> H G --> H H --> I[Browser loads
only needed chunks] style B fill:#4a90d9,color:#fff style C fill:#27ae60,color:#fff style D fill:#e67e22,color:#fff

Vite Configuration

// vite.config.js — Code splitting configuration
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
    plugins: [react()],

    build: {
        // Output directory
        outDir: 'dist',

        // Generate source maps for debugging
        sourcemap: false,

        // Configure Rollup for custom splitting
        rollupOptions: {
            output: {
                // Manual chunk configuration
                manualChunks: {
                    // Vendor chunk
                    vendor: ['react', 'react-dom', 'react-router-dom'],

                    // UI library chunk
                    'vendor-ui': ['@mui/material', '@emotion/react'],

                    // Utility libraries
                    'vendor-utils': ['lodash-es', 'date-fns', 'axios'],
                },

                // Chunk file naming
                entryFileNames: 'assets/[name].[hash].js',
                chunkFileNames: 'assets/[name].[hash].js',
                assetFileNames: 'assets/[name].[hash][extname]',
            },
        },

        // Chunk size warning limit
        chunkSizeWarningLimit: 500, // KB

        // Reduce CSS duplication
        cssCodeSplit: true,

        // Minification
        minify: 'esbuild', // or 'terser' for better output
    },
});

Dynamic Imports in Vite

// Vue 3 with Vite — Dynamic imports
const routes = [
    {
        path: '/',
        name: 'Home',
        // Vite automatically splits these into separate chunks
        component: () => import('@/views/Home.vue'),
    },
    {
        path: '/dashboard',
        name: 'Dashboard',
        component: () => import('@/views/Dashboard.vue'),
    },
    {
        path: '/analytics',
        name: 'Analytics',
        // Named chunks with Vite comments
        component: () => import(/* @vite-ignore */ '@/views/Analytics.vue'),
    },
];

// React with Vite — Same API
const routes = [
    {
        path: '/dashboard',
        element: (
            <Suspense fallback={<Loading />}>
                {React.lazy(() => import('./pages/Dashboard'))}
            </Suspense>
        ),
    },
    {
        path: '/settings',
        element: (
            <Suspense fallback={<Loading />}>
                {React.lazy(() => import('./pages/Settings'))}
            </Suspense>
        ),
    },
];

Manual Chunks Optimization

// vite.config.js — Advanced manual chunks
import { defineConfig } from 'vite';

export default defineConfig({
    build: {
        rollupOptions: {
            output: {
                manualChunks(id) {
                    // React ecosystem
                    if (id.includes('node_modules/react')) {
                        return 'vendor-react';
                    }

                    // UI libraries
                    if (id.includes('node_modules/@mui') ||
                        id.includes('node_modules/@emotion')) {
                        return 'vendor-ui';
                    }

                    // Chart libraries — Group together
                    if (id.includes('node_modules/chart.js') ||
                        id.includes('node_modules/d3') ||
                        id.includes('node_modules/recharts')) {
                        return 'vendor-charts';
                    }

                    // Lodash — Separate chunk (changes rarely)
                    if (id.includes('node_modules/lodash') ||
                        id.includes('node_modules/lodash-es')) {
                        return 'vendor-lodash';
                    }

                    // Date libraries
                    if (id.includes('node_modules/date-fns') ||
                        id.includes('node_modules/moment')) {
                        return 'vendor-dates';
                    }

                    // Everything else in node_modules
                    if (id.includes('node_modules')) {
                        return 'vendor';
                    }
                },
            },
        },
    },
});

Vite vs Webpack Comparison

// Comparison of Vite vs Webpack approaches
const comparison = {
    development: {
        vite: 'Native ESM — instant start, no bundling needed',
        webpack: 'Full bundle on every change — slower for large apps'
    },
    codeSplitting: {
        vite: 'Automatic via dynamic imports, manual chunks via rollupOptions',
        webpack: 'Requires splitChunks configuration, more control'
    },
    chunkNaming: {
        vite: 'Automatic with hash, or manual via manualChunks',
        webpack: 'Magic comments, splitChunks.name, content hashes'
    },
    configuration: {
        vite: 'Minimal config needed for common cases',
        webpack: 'More config required for optimal splitting'
    },
    buildSpeed: {
        vite: 'Faster for small-medium projects (Rollup)',
        webpack: 'Better caching for large projects'
    },
    outputSize: {
        vite: 'Comparable to Webpack, slightly smaller in many cases',
        webpack: 'Comparable, better for complex tree-shaking'
    }
};

Monitoring Vite Bundle

// vite.config.js — Bundle visualization
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
    plugins: [
        visualizer({
            filename: 'dist/bundle-visualization.html',
            title: 'Vite Bundle Analysis',
            template: 'treemap', // sunburst, treemap, network
            gzipSize: true,
            brotliSize: true,
        }),
    ],
});

// package.json scripts
{
    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "preview": "vite preview",
        "analyze": "vite build && open dist/bundle-visualization.html",
        "build:stats": "vite build --output-stats stats.json"
    }
}

Common Mistakes

  1. Not configuring manualChunks for large dependencies. Vite's auto-splitting is good but manual chunks give predictable names for CDN Caching. Configure manualChunks for your major dependencies.
  2. Importing from barrel files (index.js) in dynamic imports. Barrel files re-export many modules. Dynamic imports of barrel files include all re-exports. Import directly from the specific module file.
  3. Forgetting to handle chunk loading errors. Dynamic imports can fail on slow networks. Wrap in try-catch and show fallback UI.
  4. Not analyzing the bundle after adding dependencies. A single new npm package can add 100KB. Run bundle analysis after major additions.
  5. Using Webpack magic comments in Vite. Vite uses Rollup, not Webpack. Magic comments like webpackChunkName don't work. Use Vite's manualChunks configuration instead.

Practice Questions

  1. How does Vite handle dynamic imports differently from Webpack?
  2. How do you configure manual chunks in Vite?
  3. How do you analyze Vite's bundle output?
  4. What is the advantage of Vite's ESM-based development approach?
  5. How do manualChunks in Vite compare to splitChunks in Webpack?

Challenge: Migrate a Webpack-based project to Vite: configure rollupOptions for manual chunks, set up bundle visualization, verify chunk sizes match Webpack output, and benchmark development start time.

FAQ

Does Vite support React.lazy?

Yes. Vite supports React.lazy and Suspense out of the box. Dynamic imports trigger automatic code splitting exactly like in Webpack.

How does Vite handle CSS code splitting?

Vite splits CSS per entry point by default. Each dynamically imported module's CSS is extracted into a separate file and loaded when the module loads.

Can I use Webpack-specific features with Vite?

No. Vite uses Rollup under the hood. Webpack-specific plugins and configurations (splitChunks, loaders) don't work. Use Vite/Rollup equivalents.

Is Vite faster than Webpack for production builds?

Vite's Rollup-based builds are faster for small-medium projects. Webpack's caching makes it competitive for very large projects. Try both for your specific case.

Can I use Vite for library builds?

Yes. Vite's library mode builds distributable packages. Use build.lib configuration for bundling libraries with proper externalization and code splitting.

Mini Project

Set up a Vite project with manual chunks: create a React app with 3 large libraries (chart, date utils, UI kit), configure manualChunks to separate them, add bundle visualization, limit vendor chunk to under 300KB, and compare build output with and without manual chunks.

What's Next

You've mastered Vite dynamic imports. Now learn about Preload & Prefetch for hinting the browser about critical and future resources.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro