Skip to content

Next.js Mockup Pages Without Login for Client Review

Adityo Guni Waluyo

Replace static mockup screenshots with real Next.js routes clients can open without a session, using an explicit public path list in the proxy file.

TL;DR

The blank preview problem came from mockup routes sitting behind auth, but loosening authentication for the whole app was never an option. Next.js's new root-level proxy file exempts mockup routes from authorization via an explicit public paths list and config matcher, leaving authentication untouched. Mockup routes should use fixture data only, and every route gets tested both ways.

The client's screen was blank. Every prototype page sat behind a login, and the static mockup images sent the week before had drifted out of sync the moment the card component changed in code. My first guess was wrong: exposing preview access meant loosening authentication for the whole application, an unacceptable risk.

The solution is not removing authentication but separating three concepts that often get flattened into one. Authentication verifies who the user is, session management tracks that state, while authorization decides which routes and data may be accessed. A mockup only needs to be exempted from authorization; the core authentication mechanism stays untouched.

The old middleware.ts convention has been replaced: Next.js now uses a proxy file at the project root or inside src, at the same level as the app folder per the official documentation. This file executes before routes render, exactly the point where a "may view or not" decision belongs.

Adding the public route list

Unscoped, the proxy runs on every request, including static assets like CSS, JavaScript, and images in the public folder. Redirect logic without a boundary can block page loads without anyone noticing. That is why execution is limited through config.matcher, and inside it the mockup routes are allowed through an explicit list:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const publicPaths = ['/mockup', '/mockup/:path*'];

export function proxy(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  const isPublic = publicPaths.some((path) => pathname.startsWith(path));

  if (isPublic) {
    return NextResponse.next();
  }

  return NextResponse.redirect(new URL('/login', request.url));
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

The pattern is verifiable on your own project: open /mockup/dashboard in an incognito window without a session and the page must render; open /dashboard and you must be redirected to login. Matchers that try to skip dotted paths are a common leak source; a pattern skipping every path containing a dot can accidentally expose API routes or admin pages. An explicit public list is more predictable, and every route family should be tested both positively and negatively.

Mockup data stays fixture

Public access carries its own risk: a mockup page must never touch the production database. Use fixture data defined directly in components or separate JSON files, and make sure no API call loads real user data when a mockup route is opened. For sessions and auth logic beyond this simple exemption, Next.js recommends a battle-tested authentication library over hand-rolled session management.

Choosing between mockups as images and mockups as routes is an architecture decision. Images are fast to produce but go stale as soon as a component changes. Real routes cost some initial configuration, but what the client sees is the UI actually running; feedback lands on the real components that will ship to production, not on an outdated screenshot. From the review side, the client opens a link, clicks through the flow, and comments on exactly what the team is building.

Sources

Next.js Documentation: Proxy (middleware), accessed October 10, 2026.
Next.js Documentation: Authentication, accessed October 10, 2026.

Related articles