Active sessions card
A settings card listing the account's live sessions with a friendly device label and per-device revoke, wired to the user router.
An account-settings card listing every live session for the signed-in user. Each
row shows a friendly device label (derived from the raw User-Agent by a pure
helper), the IP and sign-in time, and a revoke button — except the session making
the request, which is badged This device and can't self-revoke (signing out is
the app shell's job). Revoking invalidates the query so the row disappears without
a reload. It's composed over the user router's listSessions / revokeSession
surface — the same surface the reference app's account settings use.
Requires: auth
Preview
- This device
Chrome on macOS
203.0.113.7 · Signed in 7/18/2026, 9:12:00 AM
Safari on iOS
198.51.100.22 · Signed in 7/16/2026, 8:41:00 PM
Firefox on Windows
Signed in 7/11/2026, 8:03:00 AM
"use client";import { Button } from "@/components/ui/button";import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { trpc } from "@/lib/trpc";import { deviceLabel } from "./device-label";/** Human-readable timestamp; dates cross the tRPC boundary as ISO strings. */function formatWhen(value: string | Date): string { return new Date(value).toLocaleString();}/** * An account-settings card listing every live session for the signed-in user, * composed over the user router's `listSessions` / `revokeSession` surface (the * same surface `features/auth/components/sessions-list.tsx` uses). The session * making this request is badged "This device" and has no revoke button — * signing out is the app shell's job, not a self-revoke here. Revoking any other * session invalidates the query so its row disappears without a manual reload. * The raw `User-Agent` is rendered through the pure `deviceLabel` helper. */export function ActiveSessionsCard() { const utils = trpc.useUtils(); const sessions = trpc.user.listSessions.useQuery(); const revoke = trpc.user.revokeSession.useMutation({ // Router-level invalidation per GUIDELINES §4 (no measured hotspot here). onSuccess: () => utils.user.invalidate(), }); return ( <Card> <CardHeader> <CardTitle>Active sessions</CardTitle> <CardDescription> Devices currently signed in to your account. Revoke any you don't recognise. </CardDescription> </CardHeader> <CardContent> {sessions.isPending ? ( <p className="text-sm text-muted-foreground">Loading sessions…</p> ) : sessions.isError ? ( <p role="alert" className="text-sm text-destructive"> Could not load your sessions: {sessions.error.message} </p> ) : ( <ul className="flex flex-col gap-3"> {sessions.data.map((session) => ( <li key={session.id} className="flex items-center justify-between gap-4 rounded-md border px-4 py-3" > <div className="min-w-0 text-sm"> <p className="truncate font-medium"> {deviceLabel(session.userAgent)} </p> <p className="text-muted-foreground"> {session.ipAddress ? `${session.ipAddress} · ` : ""} Signed in {formatWhen(session.createdAt)} </p> </div> {session.current ? ( <span className="shrink-0 rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-700 dark:bg-green-900/40 dark:text-green-400"> This device </span> ) : ( <Button variant="secondary" className="h-9 shrink-0" onClick={() => revoke.mutate({ token: session.token })} disabled={revoke.isPending} > Revoke </Button> )} </li> ))} {revoke.isError && ( <p role="alert" className="text-sm text-destructive"> Could not revoke that session: {revoke.error.message} </p> )} </ul> )} </CardContent> </Card> );}Offline preview
The docs site has no backend, so the preview seeds three representative sessions
(one current, two revocable) to show the device labels and the revoke
affordance. In an app the data comes from the user router.
Install
npx create-saas-starter add active-sessions-cardRun it from your app's root. add only writes new files — wiring is up to you:
import { ActiveSessionsCard } from "@/components/blocks/active-sessions-card/active-sessions-card";Two-factor setup card
A settings card to enable or disable TOTP two-factor authentication, wired to the Better Auth client with an enrollment QR and recovery codes.
API key manager
A settings panel to create, reveal once, and revoke the org's API keys, wired to the api-keys router. Degrades to an explanatory notice for non-admins.