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.
November 28, 2025Web DevelopmentTanStack

The Easiest Way to Add a Blog to TanStack Start in 2026

TanStack Start brings type-safe routing to React. Now add a type-safe blog in minutes — complete with database, API, SSR, and SEO — using Better Stack's modular plugin system.

The Easiest Way to Add a Blog to TanStack Start in 2026

Add a blog to an existing TanStack Start application by generating the BTST Blog integration, checking its routes, and completing your storage and authorization setup before deployment. This guide uses the BTST v3 integration rather than the older configuration previously shown on this page.

The generated integration includes the Blog API, database model, list and article pages, editor, tags, server loaders, and metadata. Your application still owns its database, identity policy, image uploads, and deployment.

Prerequisites#

Start from an existing TanStack Start app that already runs locally. Use Node.js 22, Tailwind CSS v4, and Radix-based shadcn/ui with CSS variables configured. Keep a clean commit so you can review changes to existing routes and providers.

Blog's generated navigation also uses the shadcn Button and dropdown menu. Add those components and Sonner if they are missing:

BASH
  1. 1
npx shadcn@4.19.1 add button dropdown-menu sonner

Render your Sonner Toaster in the application root. Preserve existing application providers when the generator proposes changes. If your project uses a custom route directory or provider structure, use the manual installation reference alongside the generated output.

Step 1: Install Better Stack#

Run the released generator from your application root:

BASH
  1. 1
  2. 2
  3. 3
  4. 4
npx @btst/codegen@3.1.2 init \
  --framework tanstack \
  --adapter memory \
  --plugins blog

This pinned generator installs Stack 3.1.1 and its selected dependency set; the generator and runtime version numbers do not have to match. Review your resulting package manifest and lockfile. Do not assume that installing a newer package makes an older tutorial's imports valid.

The memory adapter lets you evaluate Blog without a database service or migration. Its data disappears when the process restarts and is not a production storage choice. Keep this unauthenticated evaluation local until you configure authorization.

Do not add --skip-install unless you intend to install the dependencies yourself. Read conflict prompts before replacing existing files.

Step 2: Create the Backend Instance#

The generator writes src/lib/stack.ts for a conventional src/ project. Its Blog registration uses the v3 backend API:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
import { createBackendStack } from "@btst/stack/api"
import { createMemoryAdapter } from "@btst/adapter-memory"
import { blogBackendPlugin } from "@btst/stack/plugins/blog/api"

export const myStack = createBackendStack({
  basePath: "/api/data",
  plugins: { blog: blogBackendPlugin() },
  adapter: (db) => createMemoryAdapter(db)({}),
})

export const { handler, dbSchema } = myStack

This is a local evaluation configuration. It does not infer administrator permissions from the presence of an editor route. See the authorization step below before exposing writes or private drafts.

Step 3: Create the API Route#

Keep the generated catch-all route at src/routes/api/data/$.ts:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
import { createFileRoute } from "@tanstack/react-router"
import { toTanStackHandlers } from "@btst/stack/tanstack"
import { handler } from "@/lib/stack"

export const Route = createFileRoute("/api/data/$")({
  server: { handlers: toTanStackHandlers(handler) },
})

The framework helper forwards requests to the BTST handler and returns its response. Keep /api/data consistent with the backend base path and client API configuration. Do not parse and rebuild the response just to connect the route; that can lose headers, status, or a streaming body. The TanStack Start streaming handler guide explains that boundary.

Step 4: Generate the Database Schema#

Skip migrations for the memory evaluation. For a persistent application, choose the adapter that matches your database and configure it in the backend before generating a schema.

The database adapter reference covers Prisma, Drizzle, Kysely, and MongoDB. Use the CLI reference for schema generation, configuration loading, and migration commands. Review generated changes against your existing schema; generation alone does not apply a database migration. Prisma and Drizzle use their native migration tools.

Switch storage deliberately before publishing real content. A successful local editor session does not prove that data survives a restart or exists in another server instance.

Step 5: Set Up the Client#

Keep the generated stack-client.tsx, stack-client.server.ts, and stack-client.origins.ts together. The v3 client uses createClientStack, with API origins, site origins, and the query client configured at the top level. The Blog plugin receives Blog-specific options.

For a same-origin local app on port 3000, start it with explicit trusted origins:

BASH
  1. 1
  2. 2
  3. 3
BTST_SITE_URL=http://localhost:3000 \
BTST_API_URL=http://localhost:3000 \
npm run dev

Match both values to your actual development port. These are origins, so do not append /api/data or /pages. Configure the deployed origins in your host's environment before a production build and server start.

The generated server integration resolves trusted origins and forwards eligible incoming request credentials. Keep that server boundary intact. Replacing it with a hardcoded localhost URL can break deployed loaders; rebuilding it from arbitrary request headers can send credentials to the wrong destination.

Step 6: Configure React Query#

Use the generated query-client helper and review the root-layout patch. If the generator cannot safely edit your root component, it prints the manual changes to apply. “Init complete” can therefore still require a provider change.

Keep server request data isolated and reuse the browser query client as intended by the generated helper. Preserve your existing router's SSR integration and application providers. Avoid introducing a separate query client for the page loader and its rendered Blog subtree.

Step 7: Import Plugin Styles#

Confirm that your global stylesheet contains the Blog import once:

CSS
  1. 1
@import "@btst/stack/plugins/blog/css";

The import supplements your Tailwind and theme configuration. If the page has data but looks unstyled, check the loaded stylesheet and CSS variables before rewriting Blog components. The BTST Tailwind CSS troubleshooting guide separates missing package styles, missing theme tokens, and stylesheet delivery failures.

Step 8: Create the Layout Provider#

The generated src/routes/pages/route.tsx wraps the page subtree in QueryClientProvider and StackProvider. It passes the resolved stack and tanstackRouter() to the provider; framework links and navigation belong there.

The Blog uploadImage override is an explicit TODO that throws until your application supplies an implementation. Connect it to your actual upload service and return the stored image URL. Never replace that TODO with a fixed example URL and describe image uploads as working.

Step 9: Create the Page Handler#

The generated src/routes/pages/$.tsx uses createTanStackPageOptions from @btst/stack/tanstack. Keep its request-aware loader and navigation-origin wiring. The factory handles plugin route matching, loaders, metadata, hydration, and missing routes; you do not need to copy the older hand-written route.PageComponent integration.

Open /pages/blog. An empty list is expected before creating content. In your local evaluation, create a post at /pages/blog/new, publish it, and open its article URL directly in a fresh tab. Confirm that the heading and body render on a direct request as well as after client navigation.

Adding Sitemap Support#

The generator includes a sitemap route. Verify that the deployed sitemap uses the real site origin and includes public article URLs. Check a published article's canonical URL and server-rendered metadata, then confirm that drafts stay out of public discovery.

Use the TanStack Start Blog SEO guide for the full HTML, canonical, and sitemap checks. Submitting a URL to a search engine does not establish that it is indexed.

Set access rules#

The memory example above deliberately omits application authorization. Before deployment, configure BTST server authorization with your trusted identity resolver and explicit Blog permissions. Client-side visibility and an obscure editor URL do not secure API operations.

Verify anonymous and non-admin behavior for creating, editing, deleting, listing drafts, and opening a known draft URL. Test public published articles separately. Follow the authorization reference; keep any existing application's stricter rules.

Verify before deployment#

Run your application's typecheck and production build with its deployed origins configured. Then verify the direct article request, client navigation, stylesheet delivery, sitemap, image upload, data persistence, and permission checks in the actual deployment environment.

The generator output and backend/API examples here were checked with codegen 3.1.2 and its Stack 3.1.1 runtime. Those checks do not establish compatibility with every existing app, production database, or upload provider.

Continue with the Blog plugin reference and installation guide. If you are adapting a v2 integration, review the breaking changes before mixing old names such as betterStack, createStackClient, or BetterStackProvider with v3 packages.

In This Post

PrerequisitesStep 1: Install Better StackStep 2: Create the Backend InstanceStep 3: Create the API RouteStep 4: Generate the Database SchemaStep 5: Set Up the ClientStep 6: Configure React QueryStep 7: Import Plugin StylesStep 8: Create the Layout ProviderStep 9: Create the Page HandlerAdding Sitemap SupportSet access rulesVerify before deployment