Api Key List
A list of API keys with their name, prefix, creation and last use, and scopes as badges. Create key opens a dialog with a name and scopes, then shows the secret once with a Copy button and a warning; it is never shown again. Revoke needs the key name typed to confirm. Your callbacks do the work. UI permission is not security: the server must check again.
A ready-made piece, built from the library's own parts.
A block puts several components together into something you would otherwise assemble by hand. It never fetches data: you give it the data, or answer its callbacks. It is copied into your project like any component, with the parts it uses, so you can change anything.
Basic
States
Permissions
ClassNames
Installation
npx rdloom add api-key-listCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/api-key-list.jsonWorks, but without upgrade tracking.
Usage
import { ApiKeyList } from "@rdloom/react";
<div className="flex w-full justify-center">
<div className="w-full max-w-3xl">
<ApiKeyList
keys={keys}
scopes={scopes}
onCreate={async (name, chosen) => {
await wait(600);
const secret = "rk_demo_4f9c2d71a8b34e5f9a60c1d2e3b4a5f6";
setKeys((current) => [{ id: `k${current.length + 3}`, name, prefix: secret.slice(0, 12), scopes: chosen, createdAt: "2027-03-01", lastUsedAt: null }, ...current]);
return { secret };
}}
onRevoke={async (key) => {
await wait(500);
setKeys((current) => current.filter((k) => k.id !== key.id));
}}
/>
</div>
</div>API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
keysrequiredThe keys to list. prefix is the visible start of the key (never the secret). scopes are scope ids. | Array<{ id: string; name: string; prefix: string; scopes: string[]; createdAt: Date | string; lastUsedAt?: Date | string | null }> | none |
scopesrequiredThe scopes a key can have, shown as checkboxes in the create dialog and as badges (by label) in the list. | Array<{ id: string; label: string; description?: string }> | none |
titleThe heading of the list. | string | "API keys" |
headingLevelHeading level of the title, 2 to 6. | number | 2 |
localeThe locale for dates. | string | "en-US" |
stateThe data state: loading, empty (no keys, with Create key), error (with Try again) or ready. Without it an empty keys list shows the empty state. | DataState | none |
onRetryAdds a Try again button to the error state. | () => void | none |
onCreaterequiredCreates the key on your server and resolves with its secret, which is shown once. A throw keeps the dialog open with an error. | (name: string, scopes: string[]) => Promise<{ secret: string }> | none |
onRevokerequiredRevokes the key after the person typed its name; yours, async. | (key: { id: string; name: string; prefix: string; scopes: string[]; createdAt: Date | string; lastUsedAt?: Date | string | null }) => void | Promise<unknown> | none |
permissionsWhat the app allows: create and revoke. Hidden renders nothing; disabled keeps the control reachable (aria-disabled) with the reason shown and read, and nothing runs. This only changes what people see: the server must check again. | Permissions<"create" | "revoke"> | none |
classNamesExtra class names for single parts, so you can restyle one part without editing the file. Keys: root, header, title, createButton, list, item, name, prefix, meta, scopes, scope, revokeButton, dialog, form, secret, copyButton, warning, reason, status. | Partial<Record<"root" | "header" | "title" | "createButton" | "list" | "item" | "name" | "prefix" | "meta" | "scopes" | "scope" | "revokeButton" | "dialog" | "form" | "secret" | "copyButton" | "warning" | "reason" | "status", string>> | none |
Accessibility
Role list, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab: moves through Create key and each Revoke button
- In the create dialog: Tab moves through the name, the scopes, Cancel and Create key; Escape cancels
- Revoke opens a confirmation where the key name is typed; Enter confirms once it matches
Screen readers announce
- "API keys, heading level 2"
- "Create key, button"; "Create API key, dialog"
- "Copy this key now. You will not see it again."
- "Key Deploy script revoked"
What your code must do
- Keys are a list; each item names the key, its prefix, when it was created and last used, and its scopes
- The create dialog asks for a name and at least one scope; problems go in an error summary linked to the fields
- The secret is shown once, with a warning in words, and a Copy button that announces "Copied"
- Closing the dialog removes the secret from the page; it cannot be shown again
- Revoking needs the key name typed and is announced when done; focus then moves to the heading
- A disabled action stays focusable and the reason is its description
Block contract
- Data
- The existing keys (without secrets), the available scopes, an async onCreate that returns the secret and an async onRevoke.
- Data states
- loading, empty, error, ready
- Permissions
- create, revoke
- Events
- onCreate, onRevoke, onRetry
- You can replace
- classNames for each part; scopes with descriptions; title and headingLevel; locale
Guidelines
Use it when
- Letting people create and revoke the keys they use to call your API
- A developer or integrations settings page
Avoid it when
- Sessions or devices: use a list of sessions with a sign out action
- Storing the secret: show it once and keep only a hash on your server
Don't
- Treating the permissions prop as protection: UI permission is not security, so the server must check again
- Keeping the secret in app state after the dialog closed: it is shown once on purpose
- Putting the secret in the keys list: only the prefix belongs there
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-default--rd-color-surface-subtle--rd-color-surface-raised--rd-color-border-default--rd-color-text-default--rd-color-text-muted--rd-color-feedback-warning--rd-color-feedback-warning-subtle--rd-color-feedback-danger--rd-elevation-raised--rd-elevation-overlay--rd-radius-overlay