FAQ

Frequently asked questions about @arkenv/nextjs.

How do I define client-side variables?

ArkEnv uses a Flat Layout that maps directly to your .env file. Any variable prefixed with NEXT_PUBLIC_ is automatically available to the client. There is no need to nest variables into separate objects.


I was able to access a client-side variable in a server context! Is that ok?

Yes. In Next.js, environment variables prefixed with NEXT_PUBLIC_ are public and intended to be accessible everywhere.

@arkenv/nextjs permits accessing client variables in server-side contexts (like Server Components or API routes) by design. The runtime security proxy only blocks the reverse: accessing server-only variables (those without the NEXT_PUBLIC_ prefix) on the client.

Do I need to manually map client variables in runtimeEnv?

No. ArkEnv automates this mapping entirely via the withArkEnv configuration helper.

Historically (and in other environment validation libraries), you had to write a manual runtimeEnv mapping object containing keys like NEXT_PUBLIC_VAR: process.env.NEXT_PUBLIC_VAR so that the Next.js bundler could statically analyze and inline those references into the client-side build.

ArkEnv's withArkEnv helper parses your schema file at build time, identifies the client-side and shared keys, and automatically generates a tailored createEnv factory inside env.gen.ts that includes the complete, compiled runtimeEnv block. You get the benefits of automatic inlining without any manual boilerplate.

Server-side variables, on the other hand, do not need to be inlined and are read dynamically from Node's runtime environment, so the helper skips mapping them to avoid bloating the client bundle.


Can I disable code generation?

Yes. If you prefer to maintain the runtimeEnv mapping manually (or if your build environment restricts writing files during builds), you can opt out of code generation.

Pass { codegen: false } to withArkEnv to keep build-time environment variable validation active without generating env.gen.ts:

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

const : NextConfig = {};
export default (, { : false });

When codegen is disabled, import createEnv directly from @arkenv/nextjs in your schema file and provide a manual runtimeEnv mapping:

src/env.ts
import {  } from "@arkenv/nextjs";

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

By disabling codegen, you become responsible for keeping runtimeEnv in sync with your schema manually.


How does the runtime protection work under the hood?

@arkenv/nextjs relies on Next.js bundling mechanics and package conditional exports to enforce strict environment isolation based on where the code is running:

Export conditionNext.js contextAccessible variables
react-serverServer Components & Route HandlersAll variables (server, client, shared)
defaultClient Components (SSR & Browser)Public variables only (client, shared)

When your app runs on the server, the full schema is validated and all variables are available. If a client component attempts to read a server-only variable during SSR or in the browser, ArkEnv's runtime proxy intercepts the call and immediately throws a descriptive runtime error.

For projects using the strict layout, the protection goes even further: it leverages Next.js's native server-only package to prevent server-side files from being imported into client components altogether, guaranteeing server secrets are locked out of browser bundles at compile time.


When should I use the strict layout?

If the flat layout is recommended, why use the strict layout?

  1. Compile-time isolation: Next.js's native server-only package throws a compiler error if server code is imported into a client bundle. With the strict layout, this boundary is enforced by the compiler before the code even runs.
  2. Framework alignment: Next.js already forces developers to think in terms of Client Component boundaries ("use client"). Having matching separate files (env.client.ts vs env.server.ts) naturally mirrors the framework's architecture.
  3. No boilerplate penalty: The ArkEnv CLI completely eliminates the effort of setting up separate files. Running arkenv init scaffolds the entire 3-file setup with extends pre-wired.

Why is only NODE_ENV safe to expose to both client and server?

Next.js automatically replaces references to process.env.NODE_ENV with the current environment (e.g., 'production', 'development', or 'test') during build time. This makes it safe to share between server and client without causing hydration mismatches. For details, see the Next.js Environment Variables documentation.

For other variables, Next.js statically strips process.env references from client-side bundles unless they are prefixed with NEXT_PUBLIC_.

In the flat layout, the only variable ArkEnv exposes to both environments by default is NODE_ENV. If you use exposeToClient to expose a custom non-NEXT_PUBLIC_ variable with a default value (e.g., PORT defaulting to 3000 or THEME defaulting to 'dark'), the environment behaves asymmetrically:

  • On the server, ArkEnv reads the actual value from the environment (e.g., PORT = 8080).
  • On the client, Next.js strips process.env.PORT to undefined, causing ArkEnv to fall back to the default value (3000).

This asymmetry causes React hydration mismatches and corrupts client-side state. Only real public values (prefixed with NEXT_PUBLIC_) and NODE_ENV belong in the shared/client surface. If a value is server-only, leave it out of exposeToClient so it stays strictly server-only.