Adding Widgets
Adding Widgets
All dashboard widgets follow this chain:
React component → TanStack Query hook → Next.js API route (/api/widgets/<name>) → External service or Docker socket1. Create the API route
src/app/api/widgets/<name>/route.ts
import { NextResponse } from "next/server"export const dynamic = "force-dynamic"
export async function GET() { try { const res = await fetch("http://your-service/api/endpoint", { signal: AbortSignal.timeout(5000), cache: "no-store", }) if (!res.ok) throw new Error(`API ${res.status}`) const raw = await res.json()
return NextResponse.json({ // shape the response for the widget status: raw.status, value: raw.someValue, }) } catch { return NextResponse.json({ error: "unreachable" }, { status: 503 }) }}!!! warning “Security checklist” - Never forward raw service responses to the client - Never interpolate user input into service URLs - Validate and shape all data before returning
2. Create the widget component
src/components/widgets/<name>.tsx
"use client"
import { useQuery } from "@tanstack/react-query"import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"import { Skeleton } from "@/components/ui/skeleton"
export function MyWidget() { const { data, isLoading, isError } = useQuery({ queryKey: ["my-widget"], queryFn: async () => { const res = await fetch("/api/widgets/my-widget") if (!res.ok) throw new Error("unreachable") return res.json() }, refetchInterval: 30_000, })
return ( <Card className="flex flex-col h-full"> <CardHeader className="flex flex-row items-center justify-between pb-2"> <CardTitle className="text-base font-semibold">My Widget</CardTitle> </CardHeader> <CardContent className="flex-1 flex flex-col justify-center"> {isLoading ? ( <Skeleton className="h-6 w-1/2" /> ) : isError || !data ? ( <span className="text-danger text-sm">Unavailable</span> ) : ( <div>{data.value}</div> )} </CardContent> </Card> )}3. Add to the dashboard
In src/app/page.tsx (or the dashboard layout), import and place the widget in the grid.
Token reference
Use these CSS tokens for consistent styling:
| Token | Usage |
|---|---|
text-learn | Healthy / success (green) |
text-warn | Warning / degraded (orange) |
text-danger | Error / offline (red) |
text-ink-dim | Secondary labels |
text-ink-faint | Tertiary / metadata |