From be460895695a97c7b1ddc162fa0d3bfa7aa9b2cd Mon Sep 17 00:00:00 2001 From: DJP Date: Mon, 20 Apr 2026 20:25:04 -0400 Subject: [PATCH] Fix Next.js 16 build: wrap /change-password useSearchParams in Suspense MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The static prerender step bails on a CSR-only component that reads useSearchParams() outside a Suspense boundary. Caught by the first real production build (Turbopack/standalone output). Split ChangePasswordPage into an outer Suspense shell (default export) and an inner ChangePasswordForm that owns the useSearchParams() call. Fallback is null — the shell renders for ~1ms before the client hydrates the form, invisible. No behavior change. No other auth pages use useSearchParams. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/(auth)/change-password/page.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/app/(auth)/change-password/page.tsx b/src/app/(auth)/change-password/page.tsx index 5916755..12fdec1 100644 --- a/src/app/(auth)/change-password/page.tsx +++ b/src/app/(auth)/change-password/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { Suspense, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -8,7 +8,18 @@ import { Label } from "@/components/ui/label"; const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; +// Next.js 16 requires useSearchParams() consumers to live under a Suspense +// boundary so the static prerender has something to bail out into. Split the +// inner form out; the default export is just the Suspense shell. export default function ChangePasswordPage() { + return ( + + + + ); +} + +function ChangePasswordForm() { const router = useRouter(); const search = useSearchParams(); const isFirstLogin = search.get("first") === "1";