---
title: Vite
description: How to integrate varlock with Vite for secure, type-safe environment management
---
import SyncedCodeTabs from '@/components/SyncedCodeTabs.astro'
import { TabItem, Tabs, Steps } from "@astrojs/starlight/components";
import Badge from '@/components/Badge.astro';
import ExecCommandWidget from '@/components/ExecCommandWidget.astro';
import InstallJsDepsWidget from '@/components/InstallJsDepsWidget.astro';

<div class="page-badges">
  <Badge npmPackage="@varlock/vite-integration" />
</div>

Some frameworks use Vite under the hood, and some projects use Vite directly. Either way, often there is some [automatic loading of .env files](https://vite.dev/guide/env-and-mode.html) happening, but it is fairly limited. To integrate varlock into a Vite-powered application, you must use our [`@varlock/vite-integration`](https://www.npmjs.com/package/@varlock/vite-integration) package, which is a [Vite plugin](https://vite.dev/guide/using-plugins.html).

This plugin does a few things:
- Loading and validating your .env files using varlock, injecting resolved env into process.env at build/dev time
- Simplifies using env vars within your `vite.config.*` file
- Build time replacements of `ENV.xxx` of non-sensitive items (no prefix required)
- Within SSR contexts, injecting additional initialization code and enabling additional [security features](https://varlock.dev/guides/secrets/#security-enhancements)

:::note[Astro users]
For [Astro](https://astro.build) - which is also powered by Vite - you should use our [Astro integration](/integrations/astro/).
::::

:::note[SvelteKit users]
For [SvelteKit](https://svelte.dev/docs/kit), the plain Vite integration below works for most adapters. If you're deploying to Cloudflare Workers via `@sveltejs/adapter-cloudflare`, see our [SvelteKit guide](/integrations/sveltekit/) — it points you at the Cloudflare-specific plugin.
::::

:::caution[Deploying a non-static site to Cloudflare Workers?]
If your project has an SSR/server component that runs on Cloudflare Workers, the plain Vite integration won't wire up runtime secrets correctly — secrets would end up bundled into the worker instead of being stored as Cloudflare secrets. Use the [Cloudflare integration](/integrations/cloudflare/) (or the [SvelteKit guide](/integrations/sveltekit/) for SvelteKit) instead. This page is fine for **static-only** Vite sites.
:::

## Frameworks that use Vite ||frameworks||

If you're using [Qwik](https://qwik.dev/) or [React Router](https://reactrouter.com/)/[Remix](https://remix.run/), you can follow the Vite instructions below. If you're using [Cloudflare Workers](https://developers.cloudflare.com/workers/) with the [Cloudflare Vite plugin](https://developers.cloudflare.com/workers/vite-plugin/), use the [Cloudflare integration](/integrations/cloudflare/) instead of this page. For anything framework-specific on Cloudflare Workers (like Next.js) follow the [integration](/integrations/overview) docs for that framework.

## Setup

:::caution[Requirements]
- Node.js v22 or higher
- Vite v5 or higher
:::

<Steps>

1. **Install varlock and the Vite integration package**
    <InstallJsDepsWidget packages="@varlock/vite-integration varlock" />

1. **Run `varlock init` to set up your `.env.schema` file**

    This will guide you through setting up your `.env.schema` file, based on your existing `.env` file(s). Make sure to review it carefully.

    <ExecCommandWidget command="varlock init" showBinary={false} />

1. **Enable the Vite config plugin**

    You must add our `varlockVitePlugin` to your `vite.config.*` file:

    ```diff lang="ts" title="vite.config.ts" add="varlockVitePlugin()"
    import { defineConfig } from 'vite';
    +import { varlockVitePlugin } from '@varlock/vite-integration';

    export default defineConfig({
      plugins: [varlockVitePlugin(), otherPlugin()]
    });
    ```
</Steps>

---

## Custom .env file location

By default, varlock loads `.env` files from the current working directory. If you store your `.env` files in a custom directory, you can configure this using the `varlock.loadPath` option in your `package.json`:

```json title="package.json"
{
  "varlock": {
    "loadPath": "./envs/"
  }
}
```

This works for all varlock commands and integrations, not just Vite. The `loadPath` can point to a directory (trailing `/` recommended) or a specific `.env` file. The varlock CLI `--path` flag overrides this setting when provided. Varlock looks for this config in the `package.json` in the current working directory only — it does not walk up to parent directories.

:::tip[Directory vs file path]
Point `loadPath` to a **directory** if you want varlock to automatically load all relevant files (`.env.schema`, `.env`, `.env.local`, etc.). Pointing to a specific file will only load that file and anything it explicitly imports via `@import`.
:::

### Loading from multiple directories

You can also provide an array of paths to combine env vars from multiple locations — useful in monorepos where different packages each have their own `.env` files:

```json title="package.json"
{
  "varlock": {
    "loadPath": ["./apps/my-package/envs/", "./apps/other-package/envs/"]
  }
}
```

Each path in the array is loaded independently. Paths listed **later** in the array take higher precedence when the same variable is defined in multiple locations.

:::caution[Vite's `envDir` option is not supported]
If you are using Vite's `envDir` option to load `.env` files from a custom directory, note that **varlock ignores this option**. Use `varlock.loadPath` in your `package.json` instead, as shown above. Varlock will show a warning if it detects `envDir` is set.
:::

---

## SSR Code Injection
Within SSR builds, this plugin will automatically inject varlock initialization code into your entry points.
There are 3 modes to choose from and specify during plugin initialization. For example:

```ts
varlockVitePlugin({ ssrInjectMode: 'auto-load' })
```

- `init-only` - injects varlock initialization code, but does not load the env vars. You must still boot your app via `varlock run` in this mode.
- `auto-load` - injects `import 'varlock/auto-load';` to load your resolved env via the varlock CLI
- `resolved-env` - injects the fully resolved env data into your built code. This is useful in environments like Vercel/Cloudflare/etc where you have no control over your build command, and limited access to use CLI commands or the filesystem

**If not specified, we will attempt to infer the correct mode based on the presence of other vite plugins and environment variables, which give us hints about how your application will be run.**
Otherwise defaulting to `init-only`.


## Accessing environment variables

You can continue to use `import.meta.env.SOMEVAR` as usual, but we recommend using varlock's imported `ENV` object for better type-safety and improved developer experience:

```ts title="example.ts"
import { ENV } from 'varlock/env';

console.log(import.meta.env.SOMEVAR); // 🆗 still works
console.log(ENV.SOMEVAR);             // ✨ recommended
```

#### Why use `ENV` instead of `import.meta.env`?
- Non-string values (e.g., number, boolean) are properly typed and coerced
- All non-sensitive items are replaced at build time (not just `VITE_` prefixed ones)
- Better error messages for invalid or unavailable keys
- Enables future DX improvements and tighter control over what is bundled

### Within `vite.config.*`
It's often useful to be able to access env vars in your Vite config. Without varlock, it's a bit awkward, but varlock makes it dead simple - in fact it's already available! Just import varlock's `ENV` object and reference env vars via `ENV.SOME_ITEM` like you do everywhere else.

```diff lang="ts" title="vite.config.ts"
import { defineConfig } from 'vite';
import { varlockVitePlugin } from '@varlock/vite-integration';
+import { ENV } from 'varlock/env';

+doSomethingWithEnvVar(ENV.FOO);

export default defineConfig({ /* ... */ });
```

:::caution[TypeScript config]
If you find you are not getting type completion on `ENV`, you may need to add your vite config and generated type files (usually `env.d.ts`) to your `tsconfig.json`'s `include` array.
:::

### Within HTML templates
Vite [natively supports](https://vite.dev/guide/env-and-mode.html#html-constant-replacement) injecting env vars into HTML files using a special syntax like `%SOME_VAR%`.

This plugin injects additional replacements for strings like `%ENV.SOME_VAR%`.

Note that unlike the native functionality which does not replace missing/non-existant items, we will try to replace all items, and will throw helpful errors if something goes wrong.

:::tip[HTML comments]
Note that replacements anywhere in the file, including HTML comments, are still attempted and can cause errors. For example `<!-- %ENV.BAD_ITEM_KEY% -->` will still fail!
:::

### Within other scripts

Even in a static front-end project, you may have other scripts in your project that rely on sensitive config.

You can use [`varlock run`](/reference/cli-commands/#run) to inject resolved config into other scripts as regular environment vars.

<ExecCommandWidget command="varlock run -- node ./script.js" showBinary={false} />

### Type-safety and IntelliSense

To enable type-safety and IntelliSense for your env vars, enable the [`@generateTypes` root decorator](/reference/root-decorators/#generatetypes) in your `.env.schema`. Note that if your schema was created using `varlock init`, it will include this by default.

```diff lang="env-spec" title=".env.schema"
+# @generateTypes(lang='ts', path='env.d.ts')
# ---
# your config items...
```


---

## Managing multiple environments

Varlock can load multiple _environment-specific_ `.env` files (e.g., `.env.development`, `.env.preview`, `.env.production`) by using the [`@currentEnv` root decorator](/reference/root-decorators/#currentenv). **This is different than Vite's default behaviour, which relies on it's own [`MODE` flag](https://vite.dev/guide/env-and-mode.html#modes).**

Usually this env var will be defaulted to something like `development` in your `.env.schema` file, and you can override it by overriding the value when running commands - for example `APP_ENV=production vite build`. For a JavaScript based project, this will often be done in your `package.json` scripts.

```diff lang="json" title="package.json" add=/APP_ENV=[a-z]+/
{
  "scripts": {
    "dev": "vite dev",
    "test": "APP_ENV=test vitest",
    "build": "APP_ENV=production vite build",
    "preview": "APP_ENV=production vite preview",
  }
}
```

In some cases, you could also set the current environment value based on other vars already injected by your CI platform, like the current branch name. See the [environments guide](/guides/environments) for more information.


## Managing sensitive config values

Vite uses the `VITE_` prefix to determine which env vars are public (bundled for the browser). Varlock decouples the concept of being _sensitive_ from key names, and instead you control this with the [`@defaultSensitive`](/reference/root-decorators/#defaultsensitive) root decorator and the [`@sensitive`](/reference/item-decorators/#sensitive) item decorator. See the [secrets guide](/guides/secrets) for more information.

Set a default and explicitly mark items:

```diff lang="env-spec" title=".env.schema" add="@sensitive"
+# @defaultSensitive=false
# ---
NON_SECRET_FOO= # sensitive by default
# @sensitive
SECRET_FOO=
```

Or if you'd like to continue using Vite's prefix behavior:

```diff lang="env-spec" title=".env.schema"
+# @defaultSensitive=inferFromPrefix('VITE_')
# ---
FOO= # sensitive
VITE_FOO= # non-sensitive, due to prefix
```



:::caution[Bundling behavior]
All non-sensitive items are bundled at build time via `ENV`, while `import.meta.env` replacements continue to only include `VITE_`-prefixed items.
:::



---

## Reference
- [Root decorators reference](/reference/root-decorators)
- [Item decorators reference](/reference/item-decorators)
- [Functions reference](/reference/functions)
- [Vite environment variable docs](https://vite.dev/guide/env-and-mode.html)
