caliper uidocs

Pinned list

Free

Items you can pin to the top. Pinning moves the row up into Pinned and back again, so the eye follows it, and focus stays on the same pin button.

Installs components/ui/pinned-list.tsxDepends on lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/pinned-list.json

Pinned1

Churned this monthCustomers · 14 accounts

Saved views4

MRR by planRevenue · updated 2 min ago
Unassigned ticketsSupport · 32 open
Launch checklistProjects · 6 of 9 done
Overdue invoicesBilling · $18,240

When to use #

Use it for

  • Saved views, channels, projects or documents.
  • Lists where a few items matter more to each person.

Skip it when

  • Shared ordering that everyone sees; use the sortable list.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/pinned-list.json

Adds its 5 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { FileText, Hash, Inbox, Rocket, Users } from "lucide-react"
import { useState } from "react"
import { PinnedList, type PinnedItem } from "@/components/ui/pinned-list"

const views: PinnedItem[] = [
  { id: "mrr", title: "MRR by plan", meta: "Revenue · updated 2 min ago", icon: <Hash /> },
  { id: "churn", title: "Churned this month", meta: "Customers · 14 accounts", icon: <Users />, pinned: true },
  { id: "inbox", title: "Unassigned tickets", meta: "Support · 32 open", icon: <Inbox /> },
  { id: "launch", title: "Launch checklist", meta: "Projects · 6 of 9 done", icon: <Rocket /> },
  { id: "invoices", title: "Overdue invoices", meta: "Billing · $18,240", icon: <FileText /> },
];

export function PinnedListDemo() {
  const [items, setItems] = useState(views);
  return (
    <PinnedList
      className="w-full max-w-md"
      label="Saved views"
      items={items}
      onPinnedChange={(id, pinned) => setItems((list) => list.map((i) => (i.id === id ? { ...i, pinned } : i)))}
    />
  );
}

API reference #

PinnedList

component
PropTypeDefault
items *readonly PinnedItem[]—
onPinnedChange *(id: string, pinned: boolean) => void—
labelstring"Items"
pinnedLabelstring"Pinned"
classNamestring—

Accessibility #

It exposes status semantics, with aria-hidden, aria-pressed and aria-label kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.