KiotaPay UI
A registry of the components we build on top of shadcn/ui. The shadcn primitives stay exactly as they ship — unmodified, in components/ui/. Everything we write lives beside them in components/custom/, so an upgrade of a primitive never has to be untangled from our own changes.
Distribution works the same way shadcn's does: the CLI copies source files into your repository. There is no @kiotapay/ui package to install, no version to keep in step, and no wrapper indirection between you and the markup. Once a component lands in your project it is yours to edit.
import { StatusBadge, type PaymentStatus } from '@/components/custom/status-badge'
import { formatMoney } from '@/lib/kiota-format'
const PAYMENTS: ReadonlyArray<{ reference: string; amount: number; status: PaymentStatus }> = [
{ reference: 'KP-9F2A41', amount: 125050, status: 'success' },
{ reference: 'KP-7C1B08', amount: 48000, status: 'pending' },
{ reference: 'KP-2D9E33', amount: 990000, status: 'failed' },
{ reference: 'KP-5A0C77', amount: 32500, status: 'refunded' },
]
export default function StatusBadgeTable() {
return (
<div className="w-full overflow-x-auto">
<table className="w-full min-w-[22rem] text-sm">
<thead>
<tr className="border-b text-left text-muted-foreground">
<th className="pb-2 font-medium">Reference</th>
<th className="pb-2 text-right font-medium">Amount</th>
<th className="pb-2 text-right font-medium">Status</th>
</tr>
</thead>
<tbody>
{PAYMENTS.map((payment) => (
<tr key={payment.reference} className="border-b last:border-0">
<td className="py-2.5 font-mono text-xs">{payment.reference}</td>
<td className="py-2.5 text-right tabular-nums">{formatMoney(payment.amount)}</td>
<td className="py-2.5 text-right">
{/* Dense rows: drop the dot and step down a size. */}
<StatusBadge status={payment.status} size="sm" withDot={false} />
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}Why a registry rather than a package
A component library published to npm has to guess which props you will need, and every gap becomes a feature request. Copy-based distribution inverts that: you receive readable source and change it in place.
The trade is real and worth stating plainly — you do not get updates for free. A fix we make here reaches you only when you re-run add for that item with --overwrite, which replaces the file and any edits in it. See Updating for how to adopt changes without losing work. In exchange, a component that is 90% right is a five-minute edit instead of a fork.
What is in the registry
| Item | Type | Description |
|---|---|---|
money-input | Component | Currency-aware amount field that reads and writes minor units |
status-badge | Component | Payment status pill across paid, pending, failed, refunded and draft |
copy-field | Component | Read-only value with copy and optional masking, for keys and references |
use-copy | Hook | Clipboard write with a self-resetting copied flag |
kiota-theme | Theme | The design system: brand colours, state ramps, radius, fonts, money helpers |
kiota-style | Style | Whole-project setup for shadcn init |
kiota-theme has no page of its own under Components because you never add it directly — every component depends on it, so the CLI installs it the first time it is needed. That also means installing any component applies KiotaPay branding to the host app.
Getting started
Point your project at the registry once, then add components by name.
pnpm dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/money-input.jsonnpx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/money-input.jsonyarn dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/money-input.jsonbunx --bun shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/money-input.jsonSee Installation for the namespace setup that turns that URL into @kiota/money-input, Theming for shipping brand tokens into a host project, and How it works for what the CLI actually does when it runs.