BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 17, 2026ReactBlog

BTST Blog Loading States: Prefetched Data and Direct Page Imports

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

BTST Blog Loading States: Prefetched Data and Direct Page Imports

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.

Separate data loading from component loading#

There are several possible reasons for a skeleton:

Missing or changing inputWhat to inspect
Blog query dataServer prefetch, query keys, dehydrated state, browser query client
Page JavaScriptLazy page module and framework route chunks
Resolved identity or permission stateAuth initialization and genuine session changes
A secondary sectionIts own query, such as recent posts
A parent streaming boundaryFramework 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.

Use the direct entry inside an existing hydrated route#

When your framework has a dedicated route for an article, import the built-in page directly in that route's rendering module:

TSX
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
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:

RouteImport suffix after @btst/stack/plugins/blog/client/pages/Component
Public post listpostsPostListPage
Individual articlepostPostPage with slug
Tag listingtagTagPage 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.

Keep provider inputs stable without freezing identity#

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.

Verify with a cold browser and delayed resources#

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.

In This Post

Separate data loading from component loadingUse the direct entry inside an existing hydrated routeKeep provider inputs stable without freezing identityVerify with a cold browser and delayed resources