How to Fix "Cannot Read Properties of Undefined Reading Replace" in Next.js
The Next.js error "Cannot read properties of undefined (reading 'replace')" means you're calling .replace() on a variable that is undefined at runtime. In Next.js, this most commonly happens in Server Components or route handlers when a dynamic param, environment variable, search param, or API response field that you expected to be a string turns out to be undefined. The fix is to guard the value before calling string methods on it, either with optional chaining, a default value, or an explicit check.
// BAD: slug could be undefined if the route doesn't match.
export default async function Page({ params }: { params: { slug: string } }) {
const { slug } = await params;
const formatted = slug.replace(/-/g, " ");
return {formatted}
;
}
// GOOD: provide a fallback before calling .replace().
export default async function Page({ params }: { params: { slug: string } }) {
const { slug } = await params;
const formatted = (slug ?? "").replace(/-/g, " ");
return {formatted}
;
}
The Five Most Common Causes in Next.js
The surface-level fix is always "guard against undefined," but the root cause differs depending on where the undefined value originates. Knowing the source prevents you from patching over a real bug.
1. Dynamic route params in App Router. In Next.js 13+ App Router, params is a promise (as of Next.js 15) and its fields can be undefined for optional catch-all routes like [...slug]. If you destructure without checking, you hit this error.
2. Search params and query strings. searchParams values are string | string[] | undefined. Passing one directly to .replace() crashes when the query param is absent.
3. Environment variables. process.env.SOME_VAR is string | undefined. If the variable isn't set in .env.local or the deployment environment, you get undefined.
4. API or database response fields. A fetch response or database row might have a nullable field. If you trust the shape without validating, any string method call on a missing field throws.
5. Next.js internal pathname handling. Middleware or custom rewrites can produce undefined values when request.nextUrl.pathname is accessed before the URL is fully resolved, or when headers().get() returns null (which also lacks .replace()).
// Search params: always treat values as potentially undefined.
export default async function SearchPage({
searchParams,
}: {
searchParams: { q?: string };
}) {
const { q } = await searchParams;
// Optional chaining returns undefined instead of throwing.
const cleaned = q?.replace(/[^a-zA-Z0-9 ]/g, "") ?? "";
return Results for: {cleaned}
;
}
// Environment variables: validate at startup, not at call-site.
function getRequiredEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
// Call once at module scope so the app fails fast.
const apiBase = getRequiredEnv("API_BASE_URL");
// Now .replace() is safe because apiBase is guaranteed to be a string.
const cleanBase = apiBase.replace(/\/+$/, "");
Debugging When the Stack Trace Is Unhelpful
Next.js Server Component errors often produce minified or cryptic stack traces, especially in production. The error message says Cannot read properties of undefined (reading 'replace') but doesn't always point to your code. Sometimes it points to Next.js internals. Here's how to narrow it down.
First, search your entire codebase for .replace(. In most projects, there are fewer call sites than you'd expect. Each one is a candidate. Second, enable source maps in your Next.js config to get better stack traces in development.
// next.config.js — enable source maps for server-side debugging.
/** @type {import('next').NextConfig} */
const nextConfig = {
serverExternalPackages: [],
webpack: (config, { isServer }) => {
if (isServer) {
// Full source maps give readable stack traces.
config.devtool = "source-map";
}
return config;
},
};
module.exports = nextConfig;
When the Error Comes from Next.js Internals
Sometimes the stack trace points to node_modules/next/dist/... rather than your code. This typically means you're passing an unexpected value to a Next.js API. The two most common internal triggers are described below.
redirect() or rewrite() with an undefined URL. If you compute a redirect target dynamically and it ends up undefined, Next.js internally calls .replace() on it and throws. Validate the URL before passing it.
Middleware returning an undefined pathname. If your middleware constructs a NextResponse.rewrite() with a URL that has an undefined segment, the same internal .replace() call fails.
import { redirect } from "next/navigation";
export default async function ProfilePage({
params,
}: {
params: { username: string };
}) {
const { username } = await params;
// Guard before passing to redirect — undefined here causes the internal error.
if (!username) {
redirect("/404");
}
// Safe: username is guaranteed to be a string.
const normalized = username.replace(/[^a-z0-9_]/gi, "");
return @{normalized}
;
}
The Robust Pattern: Validate at the Boundary with Zod
The best long-term fix isn't sprinkling optional chaining everywhere. Instead, validate data at the boundary where it enters your application. Use a schema validation library like Zod to parse params, search params, and API responses once, then trust the typed result downstream. This eliminates the entire class of "undefined sneaking into string methods" bugs.
import { z } from "zod";
const paramsSchema = z.object({
slug: z.string().min(1),
});
export default async function Page({ params }: { params: { slug: string } }) {
// Parse throws a clear error if slug is missing or empty.
const { slug } = paramsSchema.parse(await params);
// Every .replace() call downstream is guaranteed safe.
const title = slug.replace(/-/g, " ");
return {title}
;
}
Key Gotcha: null vs undefined in Next.js
The ?? (nullish coalescing) operator catches both null and undefined, but optional chaining (?.) only prevents the throw. It still returns undefined. If you chain value?.replace(...) and then pass the result to another function that expects a string, you've moved the crash one level deeper. Always pair ?. with ?? "" when the downstream code requires a string.
Also remember that headers().get("x-custom") returns string | null, not undefined. Both || and ?? work here, but TypeScript's type narrowing only recognizes an explicit null check or nullish coalescing.