Strict layout
Get the best security in Next.js using strictly separated client and server schemas.
In this setup, you define your client, server, and shared schemas in separate files.
By importing arkenv from @/env/client in client-side code and @/env/server in server-side code, you enforce a strict boundary where server variables and schemas never leak to the browser. If @/env/server is accidentally imported in a Client Component, ArkEnv fails the build with a compilation error.
Setup
The easiest way to bootstrap the strict layout is with the ArkEnv CLI. It automatically configures @arkenv/nextjs for your existing Next.js project using the strict layout option.
npx @arkenv/cli@latest init --strictpnpm dlx @arkenv/cli@latest init --strictyarn dlx @arkenv/cli@latest init --strictbunx @arkenv/cli@latest init --strictYour schema
When bootstrapping with the CLI, it generates three separate schema files under src/env/:
By default, ArkEnv for Next.js uses an automatic code generation wrapper (withArkEnv) in your Next.js config to compile your runtimeEnv block. This is why the client-side schema imports from ./generated/env.gen rather than @arkenv/nextjs/client. To opt out, simply import from @arkenv/nextjs/client directly and provide your own runtimeEnv mapping.
import { } from "@arkenv/nextjs/shared";
/**
* @internal 🛑 INTERNAL SCHEMA ONLY.
* Do not import this directly. Import `env` from `./client` or `./server` instead.
*/
export const = ({
: "'development' | 'production' | 'test' = 'development'",
});import from "./generated/env.gen";
import { } from "./internal/shared";
export const = (
{
: "string = 'https://api.example.com'",
},
{
: [],
},
);import from "@arkenv/nextjs/server";
import { as } from "./client";
export const = (
{
: "string",
},
{
: [],
},
);Usage
Both env/client.ts and env/server.ts export the resolved environment as env. This lets you use consistent env.MY_VAR imports depending on where the component executes.
In Server Components / Routes
Import env from the server file. It contains all public, shared, and server-only variables:
import { } from "@/env/server";
export function () {
// Access database URL and client API URL safely
const = .;
const = .;
return `API: ${}`;
}In Client Components
Import env from the client file (env/client.ts). The boundary protection works at two levels:
- TypeScript Type Safety: If you import
envfrom the client file and try to access a server-side variable (likeDATABASE_URL), you will get a TypeScript compilation error because it is not defined in the client-safe schema:
import { } from "@/env/client";
export function () {
const = .;
// @ts-expect-error DATABASE_URL is not defined in client env
const = .DATABASE_URL;
return `API URL: ${}`;
}- Compile-Time Isolation: If you accidentally import
envfrom the server file (env/server.ts) in a Client Component (or any file imported by one), Next.js's bundler will fail the build with a compilation error. This is because@arkenv/nextjs/serverimports Next.js's nativeserver-onlypackage to block browser compilation of server code.