Use the BTST 3.1 Blog page entries while preserving hydration, provider updates, and server authorization for public content.

A blog page can have prefetched data and still show a loading state because its page component has not loaded. BTST 3.1 adds direct Blog page imports for separate framework routes, alongside the existing lazy router pages. Choose the rendering path after checking which resource is actually missing.
This guide is checked against @btst/stack@3.1.1. It assumes a working Blog integration with query hydration and the normal authorization provider. It addresses page loading; use the TanStack SEO guide for search metadata and the drafts and caching guide for public-data boundaries.
There are several possible reasons for a skeleton:
| Missing or changing input | What to inspect |
|---|---|
| Blog query data | Server prefetch, query keys, dehydrated state, browser query client |
| Page JavaScript | Lazy page module and framework route chunks |
| Resolved identity or permission state | Auth initialization and genuine session changes |
| A secondary section | Its own query, such as recent posts |
| A parent streaming boundary | Framework loading UI and providers above BTST |
myStack.raw.blog.prefetchForRoute populates the query cache from the database. It does not download the browser's page code. The existing router's route.PageComponent still lazy-loads its inner content. A warm query cache therefore does not prove every loading state should disappear.
Keep the raw prefetch boundary explicit: it does not resolve identity, evaluate authorization, or run Blog lifecycle hooks. Before serializing data into a public page, enforce the appropriate server policy. A client permission guard cannot remove private content that was already included in HTML or dehydrated state. Do not prefetch draft or edit data into publicly cached output.
When your framework has a dedicated route for an article, import the built-in page directly in that route's rendering module:
import { PostPage } from "@btst/stack/plugins/blog/client/pages/post";
export function BlogPostContent({ slug }: { slug: string }) {
return <PostPage slug={slug} />;
}
Render this where the route previously rendered its resolved Blog PageComponent, inside the existing hydration boundary. Keep StackProvider, QueryClientProvider, the resolved client runtime, and the authorized prefetched data in place. This fragment changes the page import; it is not a complete route, loader, or server authorization implementation.
The released entries are:
| Route | Import suffix after @btst/stack/plugins/blog/client/pages/ | Component |
|---|---|---|
| Public post list | posts | PostListPage |
| Individual article | post | PostPage with slug |
| Tag listing | tag | TagPage with tagSlug |
PostListPage defaults to published: true. A draft list still needs protected server loading and access control. Import only the page your route needs instead of collecting all three in a shared layout.
The direct post entry wraps the complete content with the same guarded page factory used by the router. Permission handling, error boundaries, and route error hooks remain present. The direct entry removes the registry's inner lazy import; it does not remove framework streaming or guarantee that every dependent query is already available.
Direct entries render the built-in page. If your integration relies on pageComponents replacements, keep the router path for those replacements. Both approaches are supported. Do not replace custom pages merely to eliminate a loading indicator without measuring its cause.
BTST 3.1 memoizes its provider context when its relevant inputs are unchanged. Existing applications must also keep their resolved stack and override references stable when their inputs have not changed. Generated layouts use memoization for that purpose.
In a custom layout, memoize the override object and callbacks with the values they actually read. Recreating an equivalent object on every parent render can still supply a changed reference. Conversely, an empty dependency list around changing session or router values can retain stale behavior. Do not suppress real identity updates to preserve a screenshot of the article.
The released provider implementation preserves unchanged context values while allowing actual runtime, identity, and override changes. Host providers above it can still update during hydration. The React Router hydration guide covers the separate loader-to-browser data handoff.
Test the normal published article in both server HTML and a hydrated browser. Then delay the Blog API separately from the page JavaScript. If authorized data is already hydrated, delaying the API should not erase the article; delaying a lazy page module can still reveal its fallback. Inspect network requests and visible content rather than inferring the cause from one screenshot.
Also check a missing slug, a draft as an anonymous visitor, and an authenticated identity change. A page that stays visible by skipping authorization is not a successful loading fix. Keep metadata, canonicals, public sitemap entries, and cache invalidation working after any route refactor.
The release contains focused page-loading coverage for prefetched built-in pages. The fragment here was type-checked against the published package. Your framework's route splitting, streaming boundaries, and deployment still need browser verification.
Use the Blog documentation for prefetch route keys and provider requirements, and BTST installation for the complete framework setup.