Understanding defineConfig in Vite: Static vs Dynamic Configuration
If you're working with Vite and you've stumbled across the defineConfig
function in the Vite configuration file, you might’ve noticed that there are two common ways to write it. At first glance, it might look like just a matter of style, but there’s actually more going on beneath the surface.
In this article, we’ll go through what defineConfig
does, the two primary ways you can use it, when you should use one over the other, and a few advanced considerations that can help you write cleaner, more powerful configuration files.
What Is defineConfig
?
The defineConfig
helper is provided by Vite to define the configuration object with proper type inference and editor support—especially when you're using TypeScript. It also works just as well in JavaScript projects.
It’s imported from Vite like this:
import { defineConfig } from 'vite';
Then, you can use it in either of these forms.
Option 1: Static Configuration
export default defineConfig({
plugins: [react(), tailwindcss()],
base: "/my-project/",
});
This is the most common and simplest form. You’re just passing a plain object to defineConfig
, which Vite will use as-is.
Use this when:
- You don't need environment-specific configuration.
- Your project settings are fixed.
- You're not referencing
.env
files directly insidevite.config.js
.
This is the most readable and cleanest approach when you don’t need to introduce any logic.
Option 2: Dynamic Configuration (Using a Function)
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const base = env.VITE_BASE_PATH;
return {
plugins: [react(), tailwindcss()],
...(base ? { base } : {}),
};
});
This form accepts a function that receives an object containing:
mode
: the current mode (development
,production
, etc.)command
: either'serve'
or'build'
ssrBuild
: boolean indicating if it's an SSR build
The biggest advantage here is that you can call loadEnv()
to access values from .env
files based on the mode.
Use this when:
- You need different settings per environment (e.g., dev vs production).
- You want to reference or transform values from
.env
files. - You want to inject values conditionally (like using a custom
base
path only in production).
Using loadEnv()
Properly
The function loadEnv(mode, root, prefix)
is provided by Vite to load environment variables from .env
files. The third argument (prefix
) is crucial.
By default, Vite only exposes variables that start with VITE_
to your Vite config and client-side code. So make sure your variables are named like VITE_BASE_PATH
, not just BASE_PATH
.
const env = loadEnv(mode, process.cwd(), ''); // Load all variables
If you want to be specific:
const env = loadEnv(mode, process.cwd(), 'VITE_'); // Only loads VITE_ variables
Which One Should You Use?
- Start with static config if your app is simple and doesn't rely on different environments.
- Switch to dynamic config only when you need it—typically when referencing
.env
files or having logic that depends onmode
orcommand
.
Additional Considerations
1. Access to command
The function form also gives you access to command
, which is either 'serve'
or 'build'
. This is useful when you want to do something like this:
export default defineConfig(({ command }) => ({
base: command === 'serve' ? '/' : '/production-subpath/',
}));
This lets you use a different base
in development vs production without relying on environment variables.
2. Avoid Logic in Static Config
While you technically could write:
const base = process.env.NODE_ENV === 'production' ? '/prod/' : '/';
export default defineConfig({
base,
});
This is not recommended, especially in Vite, because:
- Vite caches config aggressively.
- Using
loadEnv()
ensures you’re working with the right.env.mode
file for the current command.
3. Customizing resolve.alias
, define
, etc.
You can also use the function form to dynamically define things like:
define: {
__APP_VERSION__: JSON.stringify(env.VITE_APP_VERSION),
}
This lets you embed environment data into your code during build time.
4. Client-Side Access to Env Variables
Remember: only variables starting with VITE_
are available to your client-side code. You can access them via import.meta.env
.
console.log(import.meta.env.VITE_BASE_PATH);
Finally
Vite gives you a lot of flexibility with its config file, and knowing the difference between static and dynamic defineConfig
usage is key to writing scalable and maintainable frontend builds.
Use static config as your default—and move to dynamic config only when your project demands it. This approach helps keep your config clean, performant, and easy to debug.
If you're still unsure when to switch to dynamic mode or how to structure .env
files properly, feel free to comment below. There’s more to explore—like cascading .env
priorities, advanced plugin configuration, or even multi-project Vite workspaces.
Comments ()