Skip to content

Repository files navigation

@btst/stack — BTST

Installable full-stack features for React apps
Framework-agnostic. Database-flexible. No lock-in.

npm MIT

Docs · Examples · Issues


What is BTST?

BTST lets you install production-ready app features as npm packages.

Instead of spending weeks building the same things again and again
(routes, APIs, database schemas, SSR, SEO, forms…):

npm install @btst/stack

Enable the features you need and keep building your product.

Available plugins

Plugin Description
Blog Content management, editor, drafts, publishing, SEO, RSS feeds
AI Chat AI-powered chat with conversation history, streaming, and customizable models
CMS Headless CMS with custom content types, Zod schemas, and auto-generated forms
Form Builder Dynamic form builder with drag-and-drop editor, submissions, and validation
UI Builder Visual drag-and-drop page builder with component registry and public rendering
Kanban Project management with boards, columns, tasks, drag-and-drop, and priority levels
Media Media library with uploads, folders, picker UI, URL registration, and reusable image inputs
OpenAPI Auto-generated API documentation with interactive Scalar UI
Route Docs Auto-generated client route documentation with interactive navigation
Comments Commenting system with moderation, likes, and nested replies

Full-stack plugins ship separate frontend and backend definitions: routes, APIs, database models, React components, SSR, and SEO—already wired. Intentional one-sided plugins stay one-sided: OpenAPI is backend-only, Route Docs is client-only, and UI Builder is client-only over CMS.

Want a specific plugin? Open an issue and let us know!


Why use it?

  • Installable features – real product features, not just UI
  • Framework-agnostic – Next.js, React Router, TanStack Router, Remix
  • Database-flexible – Prisma, Drizzle, Kysely, MongoDB
  • Zero boilerplate – no manual route or API wiring
  • Type-safe – end-to-end TypeScript

You keep your codebase, database, and deployment.


Minimal setup (Next.js)

import { createBackendStack } from "@btst/stack/api"
import { blogBackendPlugin } from "@btst/stack/plugins/blog/api"
import { createMemoryAdapter } from "@btst/adapter-memory"

function createAppStack() {
  return createBackendStack({
    basePath: "/api/data",
    plugins: {
      blog: blogBackendPlugin()
    },
    adapter: (db) => createMemoryAdapter(db)({})
  })
}

type AppStack = ReturnType<typeof createAppStack>
const globalForStack = globalThis as typeof globalThis & {
  __btst_stack__?: AppStack
}
export const myStack = globalForStack.__btst_stack__ ??= createAppStack()
export const { handler, dbSchema } = myStack
import {
  createClientStack,
  type ClientPluginEndpointOverride,
} from "@btst/stack/client"
import { blogClientPlugin } from "@btst/stack/plugins/blog/client"
import type { QueryClient } from "@tanstack/react-query"

export interface StackClientOptions {
  apiOrigin: string
  siteOrigin: string
}

export function createAppClientStack(
  queryClient: QueryClient,
  options: StackClientOptions & { headers?: HeadersInit },
) {
  const { apiOrigin, siteOrigin } = options
  const crossOriginBlogEndpoint = apiOrigin === siteOrigin
    ? undefined
    : {
        api: {
          baseURL: apiOrigin,
          basePath: "/api/data",
          credentials: "include",
        },
      } satisfies ClientPluginEndpointOverride

  return createClientStack({
    api: {
      baseURL: apiOrigin,
      basePath: "/api/data",
      ...(options.headers ? { headers: options.headers } : {}),
    },
    site: { baseURL: siteOrigin, basePath: "/pages" },
    queryClient,
    plugins: {
      blog: blogClientPlugin()
    },
    ...(crossOriginBlogEndpoint
      ? { endpoints: { blog: crossOriginBlogEndpoint } }
      : {}),
  })
}

export function getStackClient(
  queryClient: QueryClient,
  options: StackClientOptions,
) {
  return createAppClientStack(queryClient, options)
}

The generated lib/stack-client.server.ts resolves these origins from trusted deployment configuration (BTST_API_URL and BTST_SITE_URL), defaults the API to the trusted site origin, and forwards filtered credentials only to that API. It fails closed in production if no trusted origin is available. Existing same-origin Next.js installs may keep NEXT_PUBLIC_BASE_URL while migrating; new deployments should prefer the separate site/API variables.

Use the v3 framework entry factories for the two catch-all routes:

import { toNextRouteHandlers } from "@btst/stack/next"
import { handler } from "@/lib/stack"

export const { GET, POST, PUT, PATCH, DELETE } =
  toNextRouteHandlers(handler)
import { createNextPage } from "@btst/stack/next"
import { headers } from "next/headers"
import { getStackClientForRequest } from "@/lib/stack-client.server"
import { getOrCreateQueryClient } from "@/lib/query-client"

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

The request layout at app/(request)/pages/layout.tsx hydrates trusted client origins into the shared provider in app/pages/client-layout.tsx. Put SSG/ISR routes under app/(static)/pages with a header-free layout; route groups do not change the public /pages/* URLs.

Wrap the pages subtree with one StackProvider:

// app/pages/client-layout.tsx
"use client"

function PagesClientLayout({ children, clientOrigins }: {
  children: React.ReactNode
  clientOrigins: StackClientOptions
}) {
  const queryClient = getOrCreateQueryClient()
  const clientStack = useMemo(
    () => getStackClient(queryClient, clientOrigins),
    [clientOrigins.apiOrigin, clientOrigins.siteOrigin, queryClient],
  )

  return (
    <QueryClientProvider client={queryClient}>
      <StackProvider
        stack={clientStack}
        router={nextRouter()}
        auth={authProvider}
        overrides={{ blog: { uploadImage } }}
      >
        {children}
      </StackProvider>
    </QueryClientProvider>
  )
}

API, site, and QueryClient runtime belong on the resolved client stack; router and auth services belong on the provider. Plugin overrides contain only plugin-specific customization. See the full installation guide for QueryClient wiring, database adapters, all three frameworks, and auth.

Database schemas & migrations

Generate schemas and run migrations through the v3 codegen CLI. It runs the aligned Better DB CLI in isolation, so its dependencies and btst binary do not enter your application graph:

npx @btst/codegen@0.2.0 generate --orm drizzle --config lib/stack.ts --output db/schema.ts

Supports Prisma, Drizzle, MongoDB and Kysely SQL dialects.


Shadcn Registry

Each plugin's UI layer is available as a shadcn registry block. Use it to eject and fully customize the page components while keeping all data-fetching and API logic from @btst/stack:

# Install a single plugin's UI (for example, Media)
npx shadcn@latest add https://github.com/better-stack-ai/better-stack/blob/main/packages/stack/registry/btst-media.json

# Or install the full collection
npx shadcn@latest add https://github.com/better-stack-ai/better-stack/blob/main/packages/stack/registry/registry.json

Components are copied into src/components/btst/{plugin}/client/ — all relative imports remain valid and you can edit them freely.


AI Agent Skills

If you're using an AI coding agent (Cursor, Claude Code, VS Code, OpenAI Codex etc.) you can install the BTST integration skill so your agent understands the plugin system, adapter setup, and wiring patterns out of the box:

npx skills@latest add better-stack-ai/better-stack/.agents/skills/btst-integration

Or manually copy skills/btst-integration/SKILL.md into your project's agent skills directory.


Live Demo

Try the interactive playground:


Learn more

Full documentation, guides, and plugin development: 👉 https://www.better-stack.ai


Contributing

Bug reports, plugin PRs, and documentation improvements are welcome. See CONTRIBUTING.md for the plugin development guide, testing instructions, and submission checklist.


If this saves you time, a ⭐ helps others find it.

MIT © olliethedev

Releases

Packages

Contributors

Languages