Next.js

Learn how to use ArkEnv in a Next.js project.

Edit on GitHub

@arkenv/nextjs validates Next.js environment variables, generates typesafe accessors, and blocks server secrets from Client Components.

For high-level architectural trade-offs, see Frameworks.

Quickstart

Scaffold Next.js integration in an existing project using the CLI:

npx arkenv init
pnpm dlx arkenv init
yarn dlx arkenv init
bunx arkenv init

The CLI detects Next.js, installs the required packages, wraps next.config with withArkEnv, gitignores .arkenv/, and creates your initial schema file importing from @/.arkenv.

Manual installation

If you prefer manual setup, install @arkenv/nextjs alongside your chosen validation engine.

ArkType engine

Install @arkenv/core and its peer dependency arktype:

npm install @arkenv/core @arkenv/nextjs arktype
pnpm add @arkenv/core @arkenv/nextjs arktype
yarn add @arkenv/core @arkenv/nextjs arktype
bun install @arkenv/core @arkenv/nextjs arktype

Standard Schema engine

If you aren't using ArkType, install @arkenv/standard:

npm install @arkenv/standard @arkenv/nextjs
pnpm add @arkenv/standard @arkenv/nextjs
yarn add @arkenv/standard @arkenv/nextjs
bun install @arkenv/standard @arkenv/nextjs

Configuration

Wrap your Next.js configuration object with withArkEnv. This plugin runs schema validation during development and builds, and generates typed accessors in .arkenv/env.gen.ts. Import that factory as @/.arkenv. Add .arkenv/ to .gitignore.

./next.config.ts
import type { NextConfig } from "next";
import {  } from "@arkenv/nextjs/config";

const : NextConfig = {};

export default ();

Function-form configs work too. withArkEnv awaits your factory, then applies aliases to the resolved object (including phase-dependent options):

./next.config.ts
import type { NextConfig } from "next";
import { withArkEnv } from "@arkenv/nextjs/config";

export default withArkEnv(async (phase, { defaultConfig }): Promise<NextConfig> => ({
  ...defaultConfig,
  reactStrictMode: phase !== "phase-test",
}));

Schema

Use a single env.ts. Client variables must use the NEXT_PUBLIC_ prefix:

./env.ts
import  from "@/.arkenv";

export const  = ({
  : "string",
  : "string = 'https://api.example.com'",
  : "'development' | 'production' | 'test' = 'development'",
});

Import env anywhere in your application. When client code accesses a server key such as DATABASE_URL, ArkEnv throws a runtime error to prevent data leaks.

If secret names or types must stay out of the client type graph as well as values, use the optional two-module recipe.

Standard Schema

If you aren't using ArkType, import withArkEnv from the /standard/config subpath:

./next.config.ts
import type { NextConfig } from "next";
import {  } from "@arkenv/nextjs/standard/config";

const : NextConfig = {};

export default ();

Then define your schema in env.ts using your validator:

./env.ts
import  from "@/.arkenv";
import * as  from "zod";

export const  = ({
  : .(),
  : .().("https://api.example.com"),
});

Read env in application code

Import { env } from "./env" in Server Components, route handlers, and Client Components.

Do not read process.env directly

Reading process.env skips ArkEnv. Import { env } so public keys stay typed and coerced and server secrets stay out of the client bundle.

./app/page.tsx
import { env } from "../env";

export default function Page() {
  return <p>{env.NEXT_PUBLIC_API_URL}</p>;
}

Reading env.DATABASE_URL from a Client Component throws. See Client vs. server.

Next steps