TanStack Start
Add the chat widget to a TanStack Start or TanStack Router app, with a server route for the token.
You’ll add the React provider to the root route and a server route that signs the token. With TanStack Router alone (no Start), add the provider the same way and put the token route in your backend.
Install
npm install @ticketping/chat-widget@next josev2 is on the next tag until 2.0.0 is released.
Add the provider
Get your publishable key (pk_...) from Settings → Widgets. It’s public.
src/routes/__root.tsx:
import { Outlet, createRootRoute } from '@tanstack/react-router'
import { TicketpingProvider } from '@ticketping/chat-widget/react'
import { useCurrentUser } from '@/lib/use-current-user'
async function getToken() {
const res = await fetch('/api/ticketping-token', { method: 'POST' })
if (!res.ok) throw new Error(`Ticketping token request failed (${res.status})`)
return res.text()
}
function RootComponent() {
const user = useCurrentUser()
return (
<TicketpingProvider
publishableKey="pk_..."
user={user ? { id: user.id, email: user.email, name: user.name } : null}
getToken={getToken}
>
<Outlet />
</TicketpingProvider>
)
}
export const Route = createRootRoute({ component: RootComponent })Keep the rest of your root route (the shellComponent, head, and so on) as it is; only wrap the <Outlet />. useCurrentUser stands for however your app reads the signed-in user, such as route context or your auth library’s hook.
The provider follows the user prop: it identifies on sign-in, switches users cleanly, and logs out when user becomes null.
The React adapter ships with 2.0. If a prop name here doesn’t match your installed beta, check the package README; the core JavaScript API works the same in every version.
Add the token route
A server route in Start signs the token. getCurrentUser is your app’s session lookup.
src/routes/api/ticketping-token.ts:
import { createFileRoute } from '@tanstack/react-router'
import { SignJWT } from 'jose'
import { getCurrentUser } from '@/lib/auth'
const secret = new TextEncoder().encode(process.env.TICKETPING_IDENTITY_SECRET)
export const Route = createFileRoute('/api/ticketping-token')({
server: {
handlers: {
POST: async ({ request }) => {
const user = await getCurrentUser(request)
if (!user) return new Response('Unauthorized', { status: 401 })
const profile: Record<string, string> = {}
if (user.email) profile.email = user.email
if (user.name) profile.name = user.name
const token = await new SignJWT(profile)
.setProtectedHeader({ alg: 'HS256' })
.setSubject(String(user.id))
.setExpirationTime('5m')
.sign(secret)
return new Response(token, { headers: { 'Content-Type': 'text/plain' } })
}
}
}
})This uses the server.handlers route option from TanStack Start 1.x. If your version uses an older API (createServerFileRoute), keep the handler body and move it there.
Set the secret
Get the identity secret from Settings → API → Identity secret.
.env:
TICKETPING_IDENTITY_SECRET=tpis_...Use the same value in every environment. Don’t prefix it with VITE_: that would ship it to the browser.
Check that it works
- Run the dev server, sign in, and open the browser console.
- Run
await (await fetch('/api/ticketping-token', { method: 'POST' })).text()and copy the token. - Paste it into the token validator under Settings → API → Identity secret. It shows the claims, or says exactly what to fix.
- Reload and send a message. The dashboard shows it with the user’s name, a Verified badge and a TEST tag.
useTicketping() from @ticketping/chat-widget/react gives your components the same helpers as Next.js, including client for the rest of the API. user may use id or userId.
Copy prompt for your AI coding agent
Add the Ticketping chat widget (v2) to this TanStack Start app.
Docs index: https://ticketping.com/llms.txt
This page as Markdown: https://ticketping.com/docs/tanstack-start.md
1. Install @ticketping/chat-widget@next and jose.
2. In src/routes/__root.tsx, wrap the <Outlet /> in
<TicketpingProvider publishableKey="pk_..." user={user} getToken={getToken}> from '@ticketping/chat-widget/react'.
user is { id, email, name } for the signed-in user from this app's auth, or null. getToken POSTs to
/api/ticketping-token and returns res.text(); throw if !res.ok. Ask me for the publishable key (starts with pk_).
3. Create the server route src/routes/api/ticketping-token.ts exactly as on the docs page, adapting only the
user lookup: 401 when signed out, otherwise a text/plain JWT from jose,
new SignJWT({ email, name }).setProtectedHeader({ alg: 'HS256' }).setSubject(String(user.id)).setExpirationTime('5m'),
signed with new TextEncoder().encode(process.env.TICKETPING_IDENTITY_SECRET).
Match the server route API of the installed @tanstack/react-start version.
4. Add TICKETPING_IDENTITY_SECRET= to .env (not committed). Never use a VITE_ prefix for it.
5. Tell me how to verify: sign in on localhost, fetch a token from the console, and paste it into the
token validator at Settings → API → Identity secret.