Dashboard

A Linear-style triage inbox for your feedback — one React component, keyboard-first.

@siteping/dashboard gives you a complete triage UI: filter, search, change statuses, delete, undo — designed for the keyboard, styled to blend into your app.

npm i @siteping/dashboard
import { SitepingInbox } from "@siteping/dashboard";

export function Triage() {
  return (
    <SitepingInbox
      projects="my-project"
      endpoint="https://my-site.com/api/siteping"
      apiKey={process.env.NEXT_PUBLIC_SITEPING_KEY}
      theme="auto"
    />
  );
}

It works with React 18 and 19, ships ESM and CJS, and has zero dependencies besides React. Give it a container with a height — the inbox fills its parent (minimum 480 px).

Props

SitepingInbox accepts every useSitepingInbox option plus these presentation props:

PropTypeDefaultWhat it does
theme"light" | "dark" | "auto""auto"auto follows the system preference — live, including mid-session changes
accentColorstring"#0066ff"Hex only; every accent shade derives from this one value. See Theming
density"comfortable" | "compact""comfortable"Row height 44 px vs 36 px
localestring"en"Any BCP-47 tag — see Languages
deepLinkParamstring"siteping"Query param used by "Open on page" links; must match the widget's deepLink param
emptyStateReactNodeCustom node for the "no feedback in this project yet" state only — the inbox-zero and no-filter-match states keep their defaults
classNamestringAppended to the root element's classes

Keyboard

Focus the list once, then:

KeyAction
j / , k / Move between rows
Enter or oOpen the focused feedback; a second Enter on an open record opens it on the live page
e / p / xToggle resolved / in progress / won't fix (pressing the current status returns it to open)
uUndo the last status change
rRefresh
/Jump to search
15Status tabs (all, open, in progress, resolved, won't fix)
?Shortcuts cheat sheet
EscIn search: clear the query, then leave search. Otherwise: close the drawer, then the cheat sheet

Shortcuts require focus inside the component (they're bound to it, not to document) and never fire while you're typing in a field — except Esc.

Statuses and actions

Four statuses: open, in_progress, resolved, wont_fix. The dashboard can set any of them (the widget's own actions stay binary — resolve/reopen). Closing a feedback (resolved or wont_fix) stamps resolvedAt; reopening clears it. Status changes are optimistic with undo; delete is permanent — it sits behind an inline confirmation and has no undo.

Languages

Same seven built-in locales as the widget (en, fr, de, es, it, pt, ru), English bundled, the rest lazy-loaded, BCP-47 tags normalized (fr-CAfr).

The dashboard also accepts custom locales at runtime:

import { registerLocale } from "@siteping/dashboard";

registerLocale("nl", { /* all 73 keys */ });
// later: <SitepingInbox locale="nl" ... />

Two rules the implementation enforces: register under the base language code ("nl", not "nl-BE" — lookups normalize to the base), and call registerLocale before the inbox mounts. The dictionary is complete by design — pass every key, and TypeScript will hold you to it on the object literal.

Edit on GitHub

On this page