Keep authenticated plugin pages and public static content under compatible layouts, with request credentials and query hydration in the right scope.

A BTST application can have public blog pages built ahead of time and account or management pages rendered for the current visitor. In Next.js, those pages need different layout boundaries even when their URLs share a /p prefix.
This guide explains the @btst/stack@3.0.2 helpers for the Next.js App Router with Cache Components disabled. The released createNextLayout explicitly targets route-segment caching in that configuration. Treat an application using cacheComponents: true as a separate integration; do not assume adding dynamic = "force-dynamic" makes these examples apply unchanged.
Use distinct route groups for the request-aware plugin catch-all and explicit public pages:
app/
(request)/
p/
layout.tsx
[[...all]]/page.tsx
(static)/
p/
layout.tsx
blog/
page.tsx
[slug]/page.tsx
p/
pages-client-layout.tsx
The group names in parentheses organize layouts without appearing in the URL. The public Blog routes remain /p/blog and /p/blog/[slug]; the catch-all handles other plugin pages. The final app/p/pages-client-layout.tsx is a shared component module, not a route or a shared layout.tsx.
Do not define the same concrete page in both groups. Keep request identity resolution out of the common root layout if public pages must remain independent of the visitor. The released page helper documents this physical split.
For an application with an existing server authorization adapter and shared client provider, the request layout can be small:
import { createNextLayout } from "@btst/stack/next/server";
import { hydrationStackAuth } from "@/lib/stack-authorization.server";
import { PagesStackProvider } from "@/app/p/pages-client-layout";
export const dynamic = "force-dynamic";
const { Layout } = createNextLayout({
auth: hydrationStackAuth,
ClientLayout: PagesStackProvider,
});
export default Layout;
hydrationStackAuth and PagesStackProvider are application modules, not package exports to install. Use your corresponding modules from the authorization setup. The provider must accept initialIdentity, pass it to StackProvider, and preserve the existing client authorization adapter.
The layout implementation reads request headers, resolves a validated identity, and passes that snapshot to the client boundary. Import it from the server entry point so the header-reading code stays in the server module graph. Do not serialize session tokens or raw request headers into client props.
The page loader needs the current request's API credentials as well as a request-scoped query client. For an installation with the generated server stack factory:
import { createNextPage } from "@btst/stack/next";
import { headers } from "next/headers";
import { getOrCreateQueryClient } from "@/lib/query-client";
import { getStackClientForRequest } from "@/lib/stack-client.server";
export const dynamic = "force-dynamic";
const page = createNextPage({
getStackClient: async (queryClient) =>
getStackClientForRequest(queryClient, {
headers: new Headers(await headers()),
}),
getQueryClient: getOrCreateQueryClient,
});
export default page.Page;
export const generateMetadata = page.generateMetadata;
getStackClientForRequest comes from your generated or equivalent server integration. The released server factory template resolves trusted API/site origins from deployment configuration and filters headers before forwarding credentials. Preserve those checks. A request's Host or forwarding header must not choose the destination that receives session credentials in production.
The helper runs the matched route loader before rendering and also before route metadata. A successful page prefetch alone does not prove the provider is using the corresponding stack. Keep the API mount, plugin set, identity partitioning where required, and query-client ownership consistent through prefetch and hydration.
On the server, use a query client scoped to the request rather than a process-wide singleton. The browser can retain its own query client across navigation. Preserve BTST's identity-change handling so cached account data is not carried into a different user's session.
The static layout can reuse the shared client provider with an anonymous initial identity. Its server path must not resolve a session from headers() or cookies(). A browser identity refresh after hydration can enable account controls, but the HTML and dehydrated data produced ahead of time must already be suitable for any visitor.
Static is a rendering choice, not an access-control policy. Select public content explicitly before prefetching it. Blog raw getters bypass operation authorization; management collections need request-authorized reads. See the public drafts and caching guide for that data boundary.
| Observation | Check |
|---|---|
| Server prefetch succeeds, then a request gets 401 | Which stack owns the later query, and whether it has the expected identity and credentials |
| A public page reads request headers during rendering | Parent layouts and imported server helpers |
| Hydration immediately refetches protected data | Query keys, query-client ownership, provider setup, and intentional stale/refetch settings |
| One account sees another account's cached result | Server query-client lifetime and browser identity partition/reset behavior |
A 401 can be the correct authorization response to a later anonymous request. Inspect the concrete request path before changing backend permissions. The product's tracked composition issue records this failure pattern; its proposed helper names and diagnostics are future work, not APIs used in this guide.
Verify with a production build, an anonymous public page, a direct authenticated management-page load, browser navigation, and a sign-out/account-switch sequence. The examples were type-checked against the stated integration; they are not a substitute for testing your application's session and data policy.
Use the installation documentation for complete framework wiring and the Better Auth UI integration guide for session/provider setup.