TanStack Start
Mount better-push in a TanStack Start app with a server route.
better-push works with TanStack Start exactly as
it does with Next.js: the core is a framework-agnostic
(request: Request) => Promise<Response> handler, and the integration is a thin
map onto TanStack Start's server routes. Everything else in the docs - the
schema, push.notify(), VAPID setup, the client SDK, the React hook - is
identical.
Fastest path
npx @better-push/cli init detects TanStack Start and writes the route below
for you. See the CLI page.
Mount the endpoints
TanStack Start server routes are web-standard: each method handler receives a
{ request } and returns a Response. toTanStackStartHandler returns the
GET/POST/PUT/PATCH/DELETE map to drop into a splat route. Every method
the router serves is exported, so the router itself decides what is allowed on a
path and new endpoints never require touching this file.
import { createFileRoute } from "@tanstack/react-router";
import { push } from "@/push";
import { toTanStackStartHandler } from "@better-push/core/tanstack";
export const Route = createFileRoute("/api/push/$")({
server: {
handlers: toTanStackStartHandler(push),
},
});The splat route /api/push/$ catches every better-push endpoint (/devices,
/devices/:id, /notifications, /notifications/unread-count,
/notifications/:id/read, /notifications/read-all, and /preferences). No
@tanstack/* types leak into the library - the integration is dependency-free
and typed structurally.
Everything else is the same
The push.ts config, the bp_* Drizzle schema, push.notify(...), the
public/sw.js service worker, and the browser client are framework-agnostic.
Follow the Quickstart for those steps; only the route file
above differs from the Next.js setup.
// Configure once (identical to the Next.js setup)
import { betterPush } from "@better-push/core";
import { drizzleAdapter } from "@better-push/core/adapters/drizzle";
import { webPush } from "@better-push/core/providers/web-push";
import { db } from "@/db";
export const push = betterPush({
database: drizzleAdapter(db),
providers: [webPush({ vapid: { /* ... */ } })],
session: async (request) => {
// Plug in TanStack Start's auth / session here.
return null;
},
});Client
The browser SDK and the usePushRegistration React hook from
@better-push/core/react are the same in a TanStack Start app - they only depend on
browser APIs and your endpoints, not on the server framework. Pass your
VAPID_PUBLIC_KEY as applicationServerKey and serve public/sw.js from your
origin.