Next.js
Learn how to use ArkEnv in a Next.js project.
@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 initpnpm dlx arkenv inityarn dlx arkenv initbunx arkenv initThe 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 arktypepnpm add @arkenv/core @arkenv/nextjs arktypeyarn add @arkenv/core @arkenv/nextjs arktypebun install @arkenv/core @arkenv/nextjs arktypeStandard Schema engine
If you aren't using ArkType, install @arkenv/standard:
npm install @arkenv/standard @arkenv/nextjspnpm add @arkenv/standard @arkenv/nextjsyarn add @arkenv/standard @arkenv/nextjsbun install @arkenv/standard @arkenv/nextjsConfiguration
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.
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):
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:
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:
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:
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.
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.