In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
I was building a multi-tenant Next.js site with Payload CMS when I hit a classic problem: editors needed instant preview updates, but production needed static performance. After trying a few patterns, I settled on one approach that gave us both without duplicating route trees. This guide walks you through the exact implementation so you can run ISR and Draft Mode on the same route.
The Problem Setup
When you run a content-heavy site, production speed depends on static generation and caching. At the same time, editors expect a live preview workflow where saving content immediately updates what they see. Most teams choose one of two paths: make pages fully dynamic and lose static performance, or build separate preview routes and maintain duplicate logic.
In this implementation, the goal is narrower and more practical: keep one route, keep static generation, and still provide live draft preview behavior for editors.
Step 1: Build a Single Route Entry for Tenant Pages
The first decision is route architecture. In this project, src/app/(frontend)/tenant-slugs/[tenant]/page.tsx is a wrapper that delegates everything to the catch-all route and shares static params generation.
The actual route logic lives in src/app/(frontend)/tenant-slugs/[tenant]/[...slug]/page.tsx, where static and dynamic behavior are configured together.
This gives you a strong base: known routes are pre-rendered, unknown routes can still render on demand, and every request goes through one code path. That one-path design is what makes the rest of this pattern clean.
With route structure in place, the next step is enabling Draft Mode safely.
Step 2: Enable Draft Mode Through a Secure API Entry Point
Draft Mode is turned on through src/app/api/draft/route.ts. Payload preview URLs call this endpoint with a secret and slug.
This works because the endpoint sets the draft cookie and sends the editor back to the normal frontend path, not a separate preview path. From here, the same route can decide whether to render draft or published data.
Step 3: Branch Behavior by Draft Mode Inside the Same Page Route
In the catch-all route, the key switch is draftMode().isEnabled. That value is read in both generateMetadata and the page component, so metadata and content stay in sync.
The route then tries specialized handlers (product, careers, project, post, collection) and falls back to general page rendering. Every handler receives the same isDraft value. That keeps behavior consistent across all content types and avoids drift between preview and production implementations.
Once the route can detect draft state, your data layer must enforce the right caching behavior.
Step 4: Split the Data Layer into Draft-Uncached and Published-Cached Paths
The core data pattern is implemented in src/payload/db/index.ts. queryPageBySlug uses an internal fetcher wrapped in React cache for request dedupe, then applies unstable_cache only for published mode.
Draft requests skip Next.js data caching and read fresh draft-aware content.
Published requests use cached fetches and are invalidated by tags/paths on publish.
The same query function handles both modes without duplicating route code.
The same draft-vs-published pattern is used across getPostBySlug, getProjectBySlug, getJobOpeningBySlug, getCollectionBySlug, getProductBySlug, and related design-data accessors. That consistency is what keeps behavior predictable.
Now that draft requests fetch fresh content, we need save-triggered refresh in the browser.
Step 5: Add Live Preview Refresh to Complete the Editor Loop
To reflect new draft saves immediately, this project uses @payloadcms/live-preview-react in a client component and calls router.refresh().
This separation is important. Saving a draft should not churn ISR caches, but publishing should invalidate both data tags and route paths. That gives editors smooth preview while keeping public caches accurate.
One more operational detail matters in this setup: tenant URL rewriting.
Step 7: Use Host-Based Tenant Rewrites While Keeping Draft Entry Separate
src/proxy.ts rewrites public URLs to internal tenant routes and intentionally excludes /api/*.
Because /api/draft is excluded, Draft Mode is enabled first. After redirect, the normal frontend path is rewritten into tenant-slugs/[tenant]/... and rendered through the same catch-all page. This is what lets preview and production share route files cleanly in a multi-tenant environment.
Conclusion
This implementation solves a specific but common problem: how to keep static/ISR performance for public traffic while supporting live draft previews for editors. The key was not adding a parallel preview route tree, but making one route draft-aware from top to bottom. Draft Mode controls request behavior, db accessors split cached and uncached paths, and publish hooks handle invalidation only when content goes live.
By the end of this setup, you can keep production pages fast and cache-friendly while editors preview draft changes on real routes with near real-time refresh.
Let me know in the comments if you have questions, and subscribe for more practical development guides.