Core

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 frameworks documentation.

Install dependencies

First, install the core package:

npm install @t3-oss/env-core zod
npm install @t3-oss/env-core zod

Create your schema

Then, you can create your schema like so:

src/env.ts
import { createEnv } from "@t3-oss/env-core";
 
export const env = createEnv({
  /*
   * Specify what prefix the client-side variables must have.
   * This is enforced both on type-level and at runtime.
   */
  clientPrefix: "PUBLIC_",
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  client: {
    PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
  },
  /**
   * What object holds the environment variables at runtime.
   * Often `process.env` or `import.meta.env`
   */
  runtimeEnv: process.env,
});
src/env.ts
import { createEnv } from "@t3-oss/env-core";
 
export const env = createEnv({
  /*
   * Specify what prefix the client-side variables must have.
   * This is enforced both on type-level and at runtime.
   */
  clientPrefix: "PUBLIC_",
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  client: {
    PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
  },
  /**
   * What object holds the environment variables at runtime.
   * Often `process.env` or `import.meta.env`
   */
  runtimeEnv: process.env,
});

For all available options, see Customization.

💡

If your framework doesn't support process.env, you'll also need to override the default skipValidation option, since that references process.env.

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:

invalid client prefix

This client prefix is also enforced at runtime to make sure validation works on both the server and client.

Validate schema on build (recommended)

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 the beginning of the build process.

Use your schema

Then, import the env object in your application and use it, taking advantage of type-safety and auto-completion:

some-api-endpoint.ts
import { env } from "~/env"; // On server
 
export const GET = async () => {
  // do fancy ai stuff
  const magic = await fetch("...", {
    headers: { Authorization: env.OPEN_AI_API_KEY },
  });
  // ...
};
some-api-endpoint.ts
import { env } from "~/env"; // On server
 
export const GET = async () => {
  // do fancy ai stuff
  const magic = await fetch("...", {
    headers: { Authorization: env.OPEN_AI_API_KEY },
  });
  // ...
};

Additional strictness for runtimeEnv

Exactly one of runtimeEnv and runtimeEnvStrict should be specified.

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

src/env.ts
import { createEnv } from "@t3-oss/env-core";
 
export const env = createEnv({
  clientPrefix: "PUBLIC_",
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  client: {
    PUBLIC_PUBLISHABLE_KEY: z.string().min(1),
  },
  /**
   * Makes sure you explicitely access **all** environment variables
   * from `server` and `client` in your `runtimeEnv`.
   */
  runtimeEnvStrict: {
    DATABASE_URL: process.env.DATABASE_URL,
    OPEN_AI_API_KEY: process.env.OPEN_AI_API_KEY,
    PUBLIC_PUBLISHABLE_KEY: process.env.PUBLIC_PUBLISHABLE_KEY,
  },
});
src/env.ts
import { createEnv } from "@t3-oss/env-core";
 
export const env = createEnv({
  clientPrefix: "PUBLIC_",
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  client: {
    PUBLIC_PUBLISHABLE_KEY: z.string().min(1),
  },
  /**
   * Makes sure you explicitely access **all** environment variables
   * from `server` and `client` in your `runtimeEnv`.
   */
  runtimeEnvStrict: {
    DATABASE_URL: process.env.DATABASE_URL,
    OPEN_AI_API_KEY: process.env.OPEN_AI_API_KEY,
    PUBLIC_PUBLISHABLE_KEY: process.env.PUBLIC_PUBLISHABLE_KEY,
  },
});

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

missing runtimeEnv