Problem
Next.js throws a TypeError at build time when the metadataBase URL is constructed from an undefined environment variable:
TypeError: Invalid URL: undefined
at new URL (<anonymous>)
at Module.default [as generateMetadata] (app/layout.tsx:12:18)
at resolveMetadata (next/dist/lib/metadata/resolve-metadata.js:1:1)
The metadata export in layout.tsx that causes the crash:
// app/layout.tsx
export const metadata: Metadata = {
metadataBase: new URL(process.env.NEXT_PUBLIC_APP_URL!),
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// undefined at build time -> TypeError
title: "My App",
description: "My application",
};
Solution
Option 1: Use nullish coalescing with a fallback URL (recommended)
// app/layout.tsx
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
export const metadata: Metadata = {
metadataBase: new URL(BASE_URL),
title: "My App",
description: "My application",
};
Option 2: Use Vercel system environment variables as fallback
// app/layout.tsx
const BASE_URL =
process.env.NEXT_PUBLIC_APP_URL ??
(process.env.VERCEL_PROJECT_PRODUCTION_URL
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
: "http://localhost:3000");
export const metadata: Metadata = {
metadataBase: new URL(BASE_URL),
title: "My App",
description: "My application",
};
Why It Works
NEXT_PUBLIC_ environment variables are inlined at build time by Next.js. If the variable is not set in the build environment, it evaluates to undefined. Calling new URL(undefined) throws a TypeError because undefined is not a valid URL string. The nullish coalescing operator (??) provides a safe fallback so the URL constructor always receives a valid string, whether or not the environment variable is defined.
Context
- Next.js 14+ metadata API with
metadataBasefor resolving relative Open Graph and canonical URLs - Common in Vercel and Netlify deployments where
.env.localis not available during CI builds - On Vercel,
VERCEL_URL(preview deployment URL) andVERCEL_PROJECT_PRODUCTION_URL(production domain) are automatically set as system environment variables -- but they are not prefixed withNEXT_PUBLIC_, so they are only available server-side - Set
NEXT_PUBLIC_APP_URLin your hosting provider's environment variable settings for production builds - The same pattern applies to any
new URL()call that depends on environment variables, such as API base URLs or OAuth redirect URIs