The shadcn Button with loading and done states built in, so the label never jumps.
Import it and pass your content. The props that matter most are listed next.
"use client";
import { Button, buttonVariants } from "@/components/ui/notchset/button";
export function EvalActions({ runEval, deploy }: { runEval: () => Promise<void>; deploy: () => void }) {
return (
<div className="flex flex-wrap items-center gap-3">
{/* Async onClick: the button shows its loading and success states on its own. */}
<Button icon="play" kbd="⌘↵" loadingLabel="Running" successLabel="Done" onClick={runEval}>
Run eval
</Button>
<Button variant="plate" icon="arrow" onClick={deploy}>
Deploy
</Button>
<a className={buttonVariants({ variant: "link" })} href="/docs">
Docs
</a>
</div>
);
}variant–
"default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "plate" | "signal"shadcn's six, plus Plate (a key that rests on a 1px plate and presses onto it) and Signal (outlined in signal, for destructive actions).
size–
"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg"32 / 24 / 28 / 36px tall, as shadcn, with 12px mono labels (13px on lg); icon sizes are squares. At least 44px tall on touch screens.
icon–
"play" | "arrow" | "plus" | "trace" | "stop" | "copy" | "external" | ReactNodeA Notchset glyph or your own svg; the scanner and check take its place.
iconPosition–
"left" | "right"Default left.
kbd–
stringA shortcut hint in a box; sets aria-keyshortcuts.
loading / success–
booleanControlled states.
loadingLabel / successLabel–
stringThe words while working and when done; the widest label sets the width.
successHold–
numberHow long an automatic success shows (ms). Default 1400.
flat–
booleanNo hover ruler, and no plate on Plate.
onClick–
(event) => unknownReturn a Promise and the button runs itself.
confirm–
stringTwo steps: the first press arms it with this label in signal; the second runs onClick. Disarms after 3s or on Esc.
hold / onHoldComplete–
number / () => voidPress and hold (ms): the key sinks onto its plate while a fill grows, then runs onHoldComplete. Release early and nothing happens.
undoMs / onUndo–
number / () => voidAfter a hold, the key reads UNDO · 5S and counts down; pressing it calls onUndo. Default 5000; 0 for none.
pressed–
booleanA toggle: filled with ink while true (aria-pressed).
count–
numberA number after the label that rolls when it changes.
progress–
number0–1: a fill grows inside the key and a rolling percentage follows the label.
disabledReason–
stringDisabled with a reason: stays focusable, shows why in a tag above it on hover and focus, ignores presses.
render (Base UI) / asChild (Radix)–
as shadcnRender as a link or your own element.
Buttondata-slot="button"
The button.
Icondata-slot="button-icon"
The icon, scanner or check.
Rulerdata-slot="button-ruler"
The hover ruler.
components/ui/notchset/button.tsx
pnpm dlx shadcn@latest add https://notchset.dev/r/button.jsonpnpm dlx shadcn@latest add https://notchset.dev/r/radix-nova/button.jsonFree, no account or key. Use the command for your project's primitives: a radix-*, new-york or default style in components.json is Radix, a base-* style is Base UI. With the @notchset registry added, @notchset/<name> picks for you.
pnpm add @base-ui/react@^1 class-variance-authority@^0.7The CLI adds the styles to the CSS file named in components.json (tailwind.css). Check that it points at your global stylesheet, that the project uses Tailwind CSS v4, and that the file is imported in your root layout. Installed by hand: paste styles.css into that file.
Give your coding assistant the Markdown guide instead of screenshots.
https://notchset.dev/api/mcp (setup)v1.0.0 · 2026-10-02
First release