Skip to content

Customizing Vite's Input and Output Directories

When working with Vite for static multipage sites, you might need to customize the default project structure for better organization. This guide covers how to change both input and output directories while ensuring all HTML pages are properly processed.

Problem: Default Structure Limitations

Vite's default structure places source files in the project root and outputs to a dist folder:

my-app/
├─ node_modules/
├─ dist/
│  ├─ assets/
│  ├─ index.html
├─ index.html
├─ main.js
├─ style.scss
├─ package.json

You want to organize your source files in a src directory and output to a dist folder at the project root:

my-app/
├─ node_modules/
├─ package.json
├─ src/
│  ├─ about.html
│  ├─ index.html
│  ├─ main.js
│  ├─ style.scss
├─ dist/
│  ├─ assets/
│  ├─ about.html
│  ├─ index.html

Basic Configuration Solution

The most straightforward solution uses Vite's configuration file to define both input and output directories:

javascript
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  root: 'src',
  build: {
    outDir: '../dist',
    emptyOutDir: true,
  }
});

TIP

Always include emptyOutDir: true to ensure the output directory is cleaned before each build, preventing stale files from accumulating.

Handling Multiple HTML Pages

For multipage applications, you need to explicitly define all entry points in the Rollup configuration:

Option 1: Manual Entry Definition

javascript
// vite.config.js
import path from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
  root: 'src',
  build: {
    outDir: '../dist',
    rollupOptions: {
      input: {
        main: path.resolve(__dirname, 'src/index.html'),
        about: path.resolve(__dirname, 'src/about.html')
      }
    }
  }
});

Option 2: Dynamic Entry Discovery

For projects with many HTML files, dynamically discover all HTML files:

javascript
// vite.config.js
import path from 'path';
import glob from 'glob';
import { defineConfig } from 'vite';

export default defineConfig({
  root: path.join(__dirname, "src"),
  build: {
    outDir: path.join(__dirname, "dist"),
    rollupOptions: {
      input: glob.sync(path.resolve(__dirname, "src", "*.html")),
    },
  },
});

WARNING

When using the glob approach, install it as a development dependency:

bash
npm install glob -D

Advanced Configuration Options

Custom Output Directory Names

If you want to change the output directory name from dist to something else (e.g., build):

javascript
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  root: 'src',
  build: {
    outDir: '../build'
  }
});

File Naming Patterns

Customize output file naming patterns:

javascript
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  root: 'src',
  build: {
    outDir: '../dist',
    rollupOptions: {
      output: {
        assetFileNames: "[name].[ext]",
        chunkFileNames: "[name].[ext]",
        entryFileNames: "[name].js",
      },
    },
  },
});

Package.json Script Updates

Update your package.json scripts to work with the new configuration:

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

INFO

With the configuration file properly set up, you no longer need to specify the source directory in your scripts.

Custom Configuration File Location

If you prefer to keep your configuration file in a different location:

json
{
  "scripts": {
    "dev": "vite --config ./config/vite.config.js",
    "build": "vite build --config ./config/vite.config.js",
    "preview": "vite preview --config ./config/vite.config.js"
  }
}

Common Issues and Solutions

Build Output Contains Only index.html

This occurs when you don't specify all HTML entry points in multi-page applications. Use the Rollup options as shown above to explicitly define all HTML files.

Assets Not Found After Build

Ensure your asset paths are relative by setting the base option:

javascript
export default defineConfig({
  base: './',
  root: 'src',
  build: {
    outDir: '../dist'
  }
});

Incorrect Path Resolution

Always use path.resolve or path.join for reliable path resolution across different environments:

javascript
import path from 'path';

export default defineConfig({
  root: path.resolve(__dirname, 'src'),
  build: {
    outDir: path.resolve(__dirname, 'dist')
  }
});

Best Practices

  1. Always use absolute paths with path.resolve() for better cross-platform compatibility
  2. Keep configuration file in project root for simplicity unless you have a specific organizational need
  3. Test both development and production builds after configuration changes
  4. Use TypeScript for your configuration file (vite.config.ts) for better type safety

By following these patterns, you can effectively customize Vite's input and output directories while maintaining a well-organized project structure for multipage applications.