
31 August 2026
The Default Value That Passed tsc and Failed the Build
Adding a tag filter to the blog listing meant giving the page component an optional searchParams prop, and the natural way to make a destructured parameter optional in TypeScript is a default value: { searchParams }: Props = {}. tsc --noEmit passed without a warning. next build failed with a type error naming a constraint that doesn't show up anywhere in application code: PageProps.
What tsc doesn't check
Next.js generates its own prop-shape validation for page components during a production build, separate from a normal tsc pass, and it's stricter about what an optional prop is allowed to look like. A default value on the parameter makes its inferred type Props | undefined in a way that satisfies plain TypeScript but violates Next's generated PageProps constraint — an error class that exists in exactly one build step and nowhere else.
The actual fix
Drop the default, and make the field itself optional in the interface instead — searchParams?: { tag?: string } with no = {} on the parameter. One existing page in the same codebase already did it this way, which meant the correct pattern was one file away the whole time; the mistake was reaching for the more familiar language-level idiom instead of checking how a sibling page had already solved the identical problem.
Why this survived a green typecheck
A CI pipeline that runs tsc --noEmit as its type-checking step and treats that as sufficient will pass this exact bug. The only way to catch it is running the real next build — the same build the deploy step runs — before merging, not a faster proxy for it. "Typechecks cleanly" and "builds cleanly" turned out to be different guarantees, and the gap between them was invisible until the slower, more expensive check actually ran.