Frameworks

Vite

Vite plugin for SPAs, SvelteKit, Remix-on-Vite, and other Vite-based frameworks.

Install

bash
npm install @goenvless/env

Add the plugin

vite.config.ts
import { defineConfig } from 'vite';
import { envless } from '@goenvless/env/vite';

export default defineConfig({
    plugins: [envless()],
});

During config resolution the plugin awaits load() and defines import.meta.env.<NAME> for every client-visibility variable whose name is a valid JavaScript identifier, plus a __ENVLESS_CLIENT_ENV__ global holding a JSON string of those same values with each one's type and visibility. define works by writing the name into your source, so a client name carrying a dash, a dot or a space is skipped and named in a warning rather than defined. Those become string literals in your client bundle at build time. Server-visibility variables are never defined for the client, so they can't leak into browser chunks.

The same load writes what it decrypted into process.env of the build process. Names that would change how a process or its children execute code, such as NODE_OPTIONS, LD_PRELOAD and PATH, are the exception: they are decrypted and handed to your code, but deliberately never written into process.env, and the loader warns with the list of names it refused.

Use it

src/main.ts
import { env } from '@goenvless/env/client';

fetch(env.PUBLIC_API_URL);   // read from the payload the plugin inlined

The /client proxy seeds itself on import from the __ENVLESS_CLIENT_ENV__ payload the plugin defined. Server-visibility variables are never in that payload, so reading one throws Missing env var: DATABASE_URL instead of returning a silent undefined, and if the plugin never ran the first read throws Envless env is not loaded yet. Import '@goenvless/env/register' or await load() first.

The proxy also applies the type you set in the dashboard, so a number variable comes back as a number and a boolean one is true for the values true and 1. import.meta.env.<NAME> is not coerced, it stays the raw string.

Server-side variables

The Vite plugin only handles the client side. For server code in an SSR framework (SvelteKit, Remix-on-Vite, or the server routes of any other Vite-based framework), load variables with the runtime loader like any Node app, by preloading @goenvless/env/register or awaiting load() before anything reads env:

src/server/db.ts
import { env, load } from '@goenvless/env/server';

await load();

export const db = createClient({ url: env.DATABASE_URL });

The loader behind @goenvless/env/server reads your .envless file and the on-disk cache through node:fs, so that entry only runs on a Node-compatible server. An edge runtime has no node:fs: use @goenvless/env/edge there, which takes the token, project, environment and key material as arguments instead of reading files.

See the Node guide for the loader options, and Workers & Edge for the edge entry.

Variable naming

Vite traditionally requires a VITE_ prefix to expose a variable to the client. With envless you don't need it. Visibility is controlled by the dashboard flag.

Keep the prefix only on variables you flag client. Vite inlines every VITE_ name it finds in process.env into client chunks whatever Envless marks it, so the plugin deletes server-visibility names carrying that prefix from the build process environment and warns with the list. If your config sets envPrefix, the plugin honours that string or array in place of VITE_.

Dev mode

The plugin runs load() once, when Vite resolves the config at vite dev startup, and the loader memoises that result for the life of the process. It doesn't hold a live connection to the API, so to pick up a variable you changed in the dashboard, restart the dev server.

A restart can still boot from the on-disk ciphertext cache under ~/.envless/cache, which counts as fresh for 15 minutes. The plugin takes no options and never asks the loader to revalidate, so inside that window delete the cached file for the project and environment, or wait the window out, before the new value shows up. See Caching & Reloads.

CI builds

Provide the full machine credential set so the plugin can fetch and decrypt during vite build:

bash
ENVLESS_TOKEN=ev_sk_xxxxxxxxxxxx \
ENVLESS_KEY=xxxxxxxxxxxx \
ENVLESS_PROJECT=my-app \
ENVLESS_ENV=production \
vite build

ENVLESS_TOKEN has to be a machine key, so it starts with ev_sk_. ENVLESS_PASSPHRASE works in place of ENVLESS_KEY, one of the two is required to decrypt. ENVLESS_PROJECT and ENVLESS_ENV can come from a committed .envless file instead, and ENVLESS_PRODUCT picks the product when the project sits under one.

See Machine & CI access for every variable and how to obtain ENVLESS_KEY.