Migrating from T3 Env

Replace createEnv and runtimeEnv with a flat ArkEnv schema.

Edit on GitHub

T3 Env is a major inspiration for ArkEnv's Next.js security model. On ArkEnv you drop the manual runtimeEnv map and keep a flat schema. For a feature cheatsheet, see Why ArkEnv?.

What changes

The table below is the mapping you need while you edit files.

FeatureT3 EnvArkEnv
Core functioncreateEnv from @t3-oss/env-nextjsarkenv from @arkenv/nextjs or @arkenv/core
Schema structureNested { server, client, runtimeEnv }Flat schema (optional two-module recipe for name isolation)
Client inliningManual runtimeEnv objectwithArkEnv writes the mapping
ValidatorZod onlyArkType via @arkenv/core, or Zod via @arkenv/standard

A flat layout still infers types and throws if client code reads a server key. Use the two-module recipe when secret names must stay out of the client graph.

Next.js migration steps

Work through these steps on an existing Next.js app.

Install ArkEnv and remove T3 Env

ArkType:

npm install @arkenv/core @arkenv/nextjs arktype
npm rm @t3-oss/env-nextjs
pnpm add @arkenv/core @arkenv/nextjs arktype
pnpm remove @t3-oss/env-nextjs
yarn add @arkenv/core @arkenv/nextjs arktype
yarn remove @t3-oss/env-nextjs
bun install @arkenv/core @arkenv/nextjs arktype
bun remove @t3-oss/env-nextjs

Zod:

npm install @arkenv/standard @arkenv/nextjs zod
npm rm @t3-oss/env-nextjs
pnpm add @arkenv/standard @arkenv/nextjs zod
pnpm remove @t3-oss/env-nextjs
yarn add @arkenv/standard @arkenv/nextjs zod
yarn remove @t3-oss/env-nextjs
bun install @arkenv/standard @arkenv/nextjs zod
bun remove @t3-oss/env-nextjs

Wrap next.config.ts

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

const : NextConfig = {};

export default ();

Without ArkType, import withArkEnv from @arkenv/nextjs/standard/config.

Replace createEnv with a flat schema

T3 Env:

./src/env.js
import { createEnv } from "@t3-oss/env-nextjs";
import * as z from "zod";

export const env = createEnv({
  server: {
    DATABASE_URL: z.url(),
  },
  client: {
    NEXT_PUBLIC_APP_URL: z.url(),
  },
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
  },
});

ArkEnv (ArkType). withArkEnv generates .arkenv/env.gen.ts:

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

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

ArkEnv (Zod):

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

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

You do not copy keys into runtimeEnv. NEXT_PUBLIC_ marks client variables.

Retarget imports

Change import { env } from "~/env" (or src/env.js) to your new ./env module.

Optional: two-module recipe

If you used two createEnv calls in T3 Env for name/type isolation, mirror that with two ArkEnv modules and two imports — not a CLI --strict flag. See Client vs. server.

Next steps