Installation

Install the core package and get started with envin.

The core package can be used in any framework of your choice. To use it, figure out what prefix your framework uses for exposing environment variables to the client. For example, Astro uses PUBLIC_*, while Vite uses VITE_*. You should be able to find this in the framework's documentation.

Install dependencies

First, install the core package:

npm install envin

Then install a schema validator. envin supports any validator that implements the Standard Schema specification:

npm install zod

Although we'll use Zod as examples throughout these docs, you can use any validator that supports Standard Schema, including Valibot, ArkType, and others.

envin requires a minimum of typescript@5.0.0.

envin is an ESM only package. Make sure that your tsconfig uses a module resolution that can read package.json#exports (Bundler is recommended).

Create your schema

Create an environment configuration file (we recommend env.config.ts, but you can name it whatever you want):

Some frameworks generate an env.d.ts file that will collide with env.ts, which means you may need to name it something else like env.config.ts.

env.config.ts
import {  } from "envin";
import {  } from "zod";

const env = ({
const env: {
    readonly NODE_ENV: "development" | "production" | "test";
    readonly DATABASE_URL: string;
    readonly OPENAI_API_KEY: string;
    readonly JWT_SECRET: string;
    readonly NEXT_PUBLIC_PUBLISHABLE_KEY: string;
    readonly _schema: FinalSchema<...>;
}
/** * Specify environment variables that can be accessed on both client and server. * You'll get a type error if you try to access a server-side env var on the client. */ : { : .(["development", "production", "test"]) .("development"), }, /** * Specify your server-side environment variables schema here. * This way you can ensure the app isn't built with invalid env vars. */ : { : .().(), : .().(1), : .().(32), }, /** * The prefix that client-side variables must have. This is enforced both at * a type-level and at runtime. */ : "NEXT_PUBLIC_", /** * Specify your client-side environment variables schema here. * For them to be exposed to the client, prefix them with your framework's public prefix. */ : { : .().(1), }, /** * What object holds the environment variables at runtime. * This is usually `process.env` or `import.meta.env`. */ : ., }); export default ;

If you only need server-side variables, you can omit the client and clientPrefix properties:

env.config.ts
import {  } from "envin";
import {  } from "zod";

export default ({
  : {
    : .().(),
    : .().(1),
  },
  : {
    : .(["development", "production", "test"]),
  },
  : .,
});

While defining both client and server schemas in a single file provides the best developer experience, it also means that your validation schemas for the server variables will be shipped to the client. If you consider the names of your variables sensitive, you should split your schemas into separate files.

env/server.ts
import {  } from "envin";
import {  } from "zod";

export const  = ({
  : {
    : .().(),
    : .().(1),
  },
  : .,
});
env/client.ts
import {  } from "envin";
import {  } from "zod";

export const  = ({
  : "NEXT_PUBLIC_",
  : {
    : .().(1),
  },
  : .,
});

For all available options, see Customization.

You'll notice that if your clientPrefix is PUBLIC_, you won't be allowed to enter any other keys in the client object without getting type-errors. Below you can see we get a descriptive error when we set VITE_PUBLIC_API_URL instead of PUBLIC_API_URL:

env.config.ts
import {  } from "envin";
import {  } from "zod";

export default ({
  : "PUBLIC_",
  : {
    VITE_PUBLIC_API_URL: .().(),
Type 'ZodString' is not assignable to type '"VITE_PUBLIC_API_URL is not prefixed with PUBLIC_."'.
}, : ., });

The steps required to validate your schema on build will vary from framework to framework, but you'll usually be able to import the env file in your configuration file, or in any file that's pulled in at the beginning of the build process.

For Next.js, you can import your env config in next.config.ts:

next.config.ts
import type { NextConfig } from "next";
import "./env.config";

const : NextConfig = {
  // Your Next.js config
};

export default ;

Note that some frameworks don't import their environment variables in their configuration file, so you may need to import your env config in a different location.

Use your environment variables

Import the env object in your application and use it with full type-safety and auto-completion:

app/api/auth/route.ts
import env from "~/env.config";

export async function POST() {
  // ✅ Fully typed and validated
  const response = await fetch("https://api.openai.com/v1/chat/completions", {
    headers: {
      Authorization: `Bearer ${env.OPENAI_API_KEY}`, // string
    },
  });

  // ✅ Access shared variables anywhere
  if (env.NODE_ENV === "development") {
    console.log("Development mode");
  }

  return Response.json({ success: true });
}
components/ClientComponent.tsx
"use client";

import env from "~/env.config";

export function ClientComponent() {
  // ✅ Works on client
  const publishableKey = env.NEXT_PUBLIC_PUBLISHABLE_KEY;

  // ❌ This will throw a runtime error on the client
  // const apiKey = env.OPENAI_API_KEY;

  return <div>Client component</div>;
}

Strict runtime - envStrict

Exactly one of envStrict or env must be provided.

If your framework doesn't bundle all environment variables by default, but instead only bundles the ones you use, you can use the envStrict option to ensure you don't forget to add any variables to your runtime:

env.config.ts
import {  } from "envin";
import {  } from "zod";

export default ({
  : {
    : .().(),
    : .().(1),
  },
  : {
    : .().(1),
  },
  : "NEXT_PUBLIC_",
  /**
   * Makes sure you explicitly access **all** environment variables
   * from `server` and `client` in your `envStrict`.
   */
  envStrict: {
Property 'DATABASE_URL' is missing in type '{ OPENAI_API_KEY: string | undefined; NEXT_PUBLIC_PUBLISHABLE_KEY: string | undefined; }' but required in type 'Record<"DATABASE_URL" | "OPENAI_API_KEY" | "NEXT_PUBLIC_PUBLISHABLE_KEY", string | number | boolean | undefined>'.
: .., : .., }, });

When using the strict option, missing any of the variables in envStrict will result in a type error.

On this page