Layouts

Strict layout

Get the best security in Nuxt using strictly separated client and server schemas.

In this setup, you define your client, server, and shared schemas in separate files.

shared.ts
client.ts
server.ts

By importing env 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 on the client side, 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/nuxt for your existing Nuxt project using the strict layout option.

npx @arkenv/cli@latest init --strict
pnpm dlx @arkenv/cli@latest init --strict
yarn dlx @arkenv/cli@latest init --strict
bunx @arkenv/cli@latest init --strict

Your schema

When bootstrapping with the CLI, it generates three separate schema files under env/:

env/internal/shared.ts
import {  } from "@arkenv/nuxt/shared";

/**
 * @internal 🛑 INTERNAL SCHEMA ONLY.
 * Do not import this directly. Import `env` from `./client` or `./server` instead.
 */
export const  = ({
	: "'development' | 'production' | 'test' = 'development'",
});
env/client.ts
import  from "@arkenv/nuxt/client";
import {  } from "./internal/shared";

export const  = (
	{
		: "string = 'https://api.example.com'",
	},
	{
		: [],
	},
);
env/server.ts
import  from "@arkenv/nuxt/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 code executes.

In server code (server routes, middleware)

Import env from the server file. It contains all public, shared, and server-only variables:

server/api/my-endpoint.ts
import {  } from "~~/env/server";

export default function (: unknown) {
	// Access database URL and client API URL safely
	const  = .DATABASE_URL;
	const  = .NUXT_PUBLIC_API_URL;
	return {  };
}

In client-side code

Import env from the client file (env/client.ts). The boundary protection works at two levels:

  1. TypeScript type safety: If you import env from the client file and try to access a server-side variable (like DATABASE_URL), you will get a TypeScript compilation error because it is not defined in the client-safe schema:
env/client.ts
import {  } from "~~/env/client";

const  = .NUXT_PUBLIC_API_URL;
// @ts-expect-error DATABASE_URL is not defined in client env
const  = .DATABASE_URL;
  1. Compile-time isolation: If you accidentally import env from the server file (env/server.ts) in client-side code, Nuxt's Vite bundler will fail the build with a compilation error. This is because @arkenv/nuxt registers a custom Vite plugin that detects imports of the server module on the client side and blocks them at build time.