SuperAPI AI Directory

btst-plugin-ssg

Frontend & websites

View repository

Updated 10-15 20:06

About this skill

BTST Plugin SSG Support

Why route.loader() fails at build time

route.loader() makes HTTP requests. No server exists during next build, so fetches fail silently — static pages render empty. Solution: expose prefetchForRoute on the raw factory to seed React Query directly from the DB.

Required files per plugin

File Purpose
api/query-key-defs.ts Shared key shapes — import into both query-keys.ts and prefetchForRoute
api/serializers.ts Convert Date fields → ISO strings before setQueryData
api/getters.ts Add any ID-based getters prefetchForRoute needs
api/plugin.ts RouteKey type + typed overloads + wire prefetchForRoute into raw factory
api/index.ts Re-export RouteKey, serializers, PLUGIN_QUERY_KEYS
query-keys.ts Import discriminator fn from api/query-key-defs.ts
client/plugin.tsx isConnectionError warn in each loader catch block

Key rules

  • Serialize Date → ISO string before every setQueryData call — DB returns Date objects, HTTP cache holds strings.
  • useInfiniteQuery lists require { pages: [...], pageParams: [...] } shape in setQueryData. Flat arrays break hydration.
  • Share key builders via api/query-key-defs.ts — never hardcode key shapes in two places.
  • One-time init steps (e.g. CMS ensureSynced) — call once at the top of prefetchForRoute; it's idempotent and safe for concurrent SSG.
  • In Next.js, keep request-aware routes under app/(request)/pages and SSG/ISR routes under app/(static)/pages. Both groups still publish /pages/* URLs.
  • Put the reusable client StackProvider shell in app/pages/client-layout.tsx. Wrap it from a request layout that resolves origins from trusted request headers and a header-free static layout so SSG does not become dynamic.

Plugins with SSG support

Plugin Prefetched keys Skipped
Blog posts, drafts, post, tag, editPost newPost
CMS dashboard, contentList, editContent newContent
Form Builder formList, editForm, submissions newForm
Kanban boards, board newBoard
AI Chat — (per-user, not static) all

isConnectionError in loader catch

import { isConnectionError } from "@btst/stack/plugins/client"

// in each loader catch block:
if (isConnectionError(error)) {
  console.warn("[btst/my-plugin] route.loader() failed — no server at build time. Use myStack.raw.myPlugin.prefetchForRoute() for SSG.")
}

Gotchas

  • route.loader() silently fails at build time — use prefetchForRoute in SSG pages instead.
  • Query key drift — always import discriminator fns from api/query-key-defs.ts; never hardcode key shapes in two places.
  • Wrong shape for infinite queries — setQueryData needs { pages: [...], pageParams: [...] }, not a flat array.
  • Dates not serialized — always pass data through a serializer before setQueryData.

Full code patterns

See REFERENCE.md for complete query-key-defs.ts, serializers.ts, prefetchForRoute overloads, and SSG page.tsx boilerplate.