cloudflare-kumo
Cloudflare's React component library for building modern web applications. Built on [Base UI](https://base-ui.com/) primitives and Tailwind CSS v4, Kumo provides 36 accessible, design-system-compliant
@cloudflare/kumo
Cloudflare's React component library for building modern web applications. Built on Base UI primitives and Tailwind CSS v4, Kumo provides 36 accessible, design-system-compliant UI components with automatic light/dark mode support via semantic color tokens. It handles keyboard navigation, focus management, and ARIA attributes out of the box.
Quick References
| File | Purpose |
|---|---|
packages/kumo/src/index.ts | Main barrel export — all components, utils, types |
packages/kumo/ai/component-registry.md | Complete component docs with props, variants, examples |
packages/kumo/ai/component-registry.json | Machine-readable component metadata (props, variants, colors) |
README.md | Installation and usage overview |
packages/kumo/src/styles/kumo.css | Kumo CSS entry point (import as @cloudflare/kumo/styles) |
When to Use
- Building React applications that need a complete, accessible UI component library with Cloudflare's design system
- Creating dashboards, admin panels, or internal tools that require components like tables, forms, dialogs, command palettes, and toast notifications
- Projects using Tailwind CSS v4 that want pre-built components with consistent theming and automatic dark mode support
- Applications that need to integrate with framework routers (React Router, Next.js) via the LinkProvider pattern
- AI-powered interfaces that need to render UI dynamically from JSON using the catalog module
Installation
npm install @cloudflare/kumo
Peer Dependencies
npm install react react-dom @phosphor-icons/react
Optional peer dependency (for catalog/JSON-UI features):
npm install zod
CSS Setup
Kumo requires its CSS to be imported alongside Tailwind CSS v4. In your application's CSS entry point:
@import "tailwindcss";
@import "@cloudflare/kumo/styles/tailwind";
Or for non-Tailwind projects, use the standalone CSS (includes Tailwind utilities):
@import "@cloudflare/kumo/styles/standalone";
In your React entry point:
import "@cloudflare/kumo/styles";
Best Practices
-
Always use semantic tokens — Use
bg-kumo-base,text-kumo-default,border-kumo-line,ring-kumo-ringinstead of raw Tailwind colors likebg-blue-500. Kumo's semantic tokens automatically adapt to light/dark mode. -
Never use the
dark:variant — Dark mode is handled automatically via CSSlight-dark()in the semantic token system. Addingdark:prefixes is redundant and may cause conflicts. -
Use
cn()for className composition — Always merge custom classes with thecn()utility exported from@cloudflare/kumoto properly handle Tailwind class conflicts viatailwind-merge. -
Provide accessible names for inputs — All form components require an accessible name. Use the
labelprop (recommended), oraria-label/aria-labelledbyfor bare inputs. Development warnings will alert you to missing labels. -
Wrap your app with
<Toasty>for toasts — The toast system requires a provider wrapping your application. Use theuseKumoToastManager()hook in child components to create notifications. -
Use
<LinkProvider>for framework routing — Wrap your app withLinkProviderto integrate Kumo's link-based components (Link, LinkButton, Breadcrumbs) with your router's<Link>component. -
Use tree-shakeable imports for smaller bundles — Import from granular paths like
@cloudflare/kumo/components/buttoninstead of the barrel export when you only need specific components.
Common Patterns
Basic Button with Variants:
import { Button, LinkButton } from "@cloudflare/kumo";
import { PlusIcon, TrashIcon } from "@phosphor-icons/react";
// Primary action
<Button variant="primary">Save Changes</Button>
// With icon
<Button variant="secondary" icon={PlusIcon}>Create Worker</Button>
// Destructive
<Button variant="destructive" icon={TrashIcon}>Delete</Button>
// Loading state
<Button variant="primary" loading>Saving...</Button>
// Icon-only (requires aria-label)
<Button shape="square" icon={PlusIcon} aria-label="Add item" />
// Link styled as button
<LinkButton href="/docs" variant="ghost">Documentation</LinkButton>
Form Input with Built-in Field Wrapper:
import { Input, Select, Checkbox, Switch } from "@cloudflare/kumo";
// Input with label, description, and validation
<Input
label="Email"
placeholder="you@example.com"
description="We'll never share your email"
error="Please enter a valid email"
variant="error"
/>
// Select dropdown
<Select label="Region" hideLabel={false} onValueChange={setRegion}>
<Select.Option value="us">United States</Select.Option>
<Select.Option value="eu">Europe</Select.Option>
</Select>
// Checkbox with label
<Checkbox label="Accept terms and conditions" />
// Switch toggle
<Switch label="Enable notifications" />
Dialog (Modal):
import { Dialog, Button } from "@cloudflare/kumo";
<Dialog.Root>
<Dialog.Trigger render={(props) => <Button {...props}>Open Dialog</Button>} />
<Dialog className="p-8" size="base">
<Dialog.Title>Confirm Action</Dialog.Title>
<Dialog.Description>Are you sure you want to proceed?</Dialog.Description>
<div className="mt-4 flex gap-2 justify-end">
<Dialog.Close render={(props) => <Button {...props}>Cancel</Button>} />
<Button variant="primary">Confirm</Button>
</div>
</Dialog>
</Dialog.Root>
Toast Notifications:
import { Toasty, useKumoToastManager, Button } from "@cloudflare/kumo";
// 1. Wrap your app with Toasty provider
function App() {
return (
<Toasty>
<MyContent />
</Toasty>
);
}
// 2. Use the toast manager in any child component
function MyContent() {
const toasts = useKumoToastManager();
return (
<Button onClick={() => toasts.add({
title: "Saved",
description: "Your changes have been saved.",
})}>
Save
</Button>
);
}
// Toast with variant and actions
toasts.add({
title: "Deployment failed",
description: "Worker build failed on line 42.",
variant: "error",
actions: [{ children: "Retry", variant: "primary", onClick: retry }],
});
// Promise-based toast
toasts.promise(deployWorker(), {
loading: { title: "Deploying..." },
success: { title: "Deployed!", description: "Worker is live." },
error: (err) => ({ title: "Failed", description: err.message }),
});
Table with Selection:
import { Table } from "@cloudflare/kumo";
<Table layout="fixed">
<Table.Header>
<Table.Row>
<Table.CheckHead
checked={allSelected}
indeterminate={someSelected}
onValueChange={toggleAll}
/>
<Table.Head>Name</Table.Head>
<Table.Head>Status</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row key={row.id} variant={selected.has(row.id) ? "selected" : "default"}>
<Table.CheckCell
checked={selected.has(row.id)}
onValueChange={() => toggle(row.id)}
/>
<Table.Cell>{row.name}</Table.Cell>
<Table.Cell>{row.status}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
Tabs Navigation:
import { Tabs } from "@cloudflare/kumo";
<Tabs
variant="segmented"
tabs={[
{ value: "overview", label: "Overview" },
{ value: "settings", label: "Settings" },
{ value: "logs", label: "Logs" },
]}
value={activeTab}
onValueChange={setActiveTab}
/>
LinkProvider Integration (React Router, Next.js, etc.):
import { LinkProvider } from "@cloudflare/kumo";
import { Link as RouterLink } from "react-router-dom";
import { forwardRef } from "react";
const AppLink = forwardRef((props, ref) => (
<RouterLink ref={ref} to={props.href ?? props.to} {...props} />
));
function App() {
return (
<LinkProvider component={AppLink}>
{/* All Kumo Link, LinkButton, Breadcrumbs now use your router */}
<MyApp />
</LinkProvider>
);
}
Responsive Grid Layout:
import { Grid, GridItem, Surface } from "@cloudflare/kumo";
<Grid variant="3up" gap="base">
<GridItem>
<Surface className="rounded-lg p-4">Card 1</Surface>
</GridItem>
<GridItem>
<Surface className="rounded-lg p-4">Card 2</Surface>
</GridItem>
<GridItem>
<Surface className="rounded-lg p-4">Card 3</Surface>
</GridItem>
</Grid>
Light/Dark Mode:
// Set mode on any parent element — all Kumo components auto-adapt
<div data-mode="dark">
<Surface className="rounded-lg p-6">
<Text variant="heading2">Dark mode content</Text>
<Button variant="primary">Action</Button>
</Surface>
</div>
// FedRAMP theme
<div data-theme="fedramp">
{/* Components use FedRAMP-compliant color tokens */}
</div>
Import Patterns
// Barrel import (all components)
import { Button, Input, Dialog, Badge } from "@cloudflare/kumo";
// Granular imports (better tree-shaking)
import { Button } from "@cloudflare/kumo/components/button";
import { Input } from "@cloudflare/kumo/components/input";
import { Dialog } from "@cloudflare/kumo/components/dialog";
// Base UI primitives (for advanced/unstyled use cases)
import { Popover } from "@cloudflare/kumo/primitives/popover";
import { Accordion } from "@cloudflare/kumo/primitives/accordion";
// Utilities
import { cn, safeRandomId } from "@cloudflare/kumo";
import { LinkProvider, useLinkComponent } from "@cloudflare/kumo";
// CSS
import "@cloudflare/kumo/styles";
API Quick Reference
Components
| Export | Category | Description |
|---|---|---|
Button | Action | Primary action trigger with variants: primary, secondary, ghost, destructive, outline |
LinkButton | Action | Anchor element styled as a button, integrates with LinkProvider |
RefreshButton | Action | Pre-built refresh icon button with loading animation |
Input | Input | Text input with optional Field wrapper (label, description, error) |
InputArea / Textarea | Input | Multi-line text input |
InputGroup | Input | Group multiple input-related elements |
Select | Input | Dropdown select with Select.Option sub-component |
Checkbox | Input | Checkbox with label support and group mode |
Switch | Input | Toggle switch with size variants (sm, base, lg) |
Radio / RadioGroup | Input | Radio button group |
Combobox | Input | Autocomplete/typeahead with single and multi-select support |
DateRangePicker | Input | Date range selection component |
SensitiveInput | Input | Password/secret input with show/hide toggle |
Field | Input | Form field wrapper with label, description, and error display |
Dialog | Overlay | Modal dialog with compound components: Root, Trigger, Title, Description, Close |
DropdownMenu | Overlay | Context menu / dropdown with items, sub-menus, checkboxes |
Popover | Overlay | Floating content panel with trigger |
Tooltip / TooltipProvider | Overlay | Hover tooltip with positioning options |
Toasty / ToastProvider | Feedback | Toast notification provider (wrap app root) |
Toast | Feedback | Individual toast notification |
Banner | Feedback | Full-width message bar (default, alert, error) |
Loader / SkeletonLine | Feedback | Animated spinner and skeleton loading line |
Badge | Display | Status label (primary, secondary, destructive, outline, beta) |
Text | Display | Typography component with heading and body variants |
Code / CodeBlock | Display | Inline code and code block display |
Meter | Display | Progress/usage meter |
Empty | Display | Empty state placeholder |
LayerCard | Display | Elevated card container |
Breadcrumbs | Display | Navigation breadcrumb trail |
CloudflareLogo | Display | Cloudflare logo with variant and color options |
Table | Display | Data table with Header, Body, Row, Cell, CheckCell, Footer |
Tabs | Navigation | Tab navigation (segmented or underline style) |
Pagination | Navigation | Page navigation controls |
MenuBar | Navigation | Horizontal menu bar |
CommandPalette | Navigation | Searchable command palette (⌘K pattern) |
Grid / GridItem | Layout | Responsive CSS grid with preset layouts (2up, 3up, 4up, etc.) |
Surface | Layout | Polymorphic container with background, shadow, and border |
Collapsible | Layout | Expandable/collapsible content section |
Link | Navigation | Styled anchor that integrates with LinkProvider |
ClipboardText | Action | Text with click-to-copy functionality |
Label | Input | Accessible label component |
Utilities
| Export | Type | Description |
|---|---|---|
cn(...classes) | Function | Merges Tailwind class names with conflict resolution (clsx + tailwind-merge) |
safeRandomId() | Function | Generates a unique ID using crypto.randomUUID when available |
LinkProvider | Component | Context provider for custom link components (router integration) |
useLinkComponent() | Hook | Returns the current link component from LinkProvider context |
useKumoToastManager() | Hook | Returns toast manager with add(), update(), promise() methods |
buttonVariants() | Function | Returns computed Tailwind classes for button variant combinations |
inputVariants() | Function | Returns computed Tailwind classes for input variant combinations |
Types
| Export | Description |
|---|---|
ButtonProps | Button props (discriminated union: text vs icon-only require aria-label) |
LinkButtonProps | Anchor-styled button props with external option |
InputProps | Input props with label, description, error for Field wrapper |
DialogProps | Dialog content panel props with size variant |
TabsProps / TabsItem | Tabs configuration with tabs array and variant |
BadgeVariant | Badge variant union: "primary" | "secondary" | "destructive" | "outline" | "beta" |
SelectProps | Select dropdown props |
CheckboxProps | Checkbox props with label and controlled/uncontrolled modes |
LinkComponentProps | Props for custom link components used with LinkProvider |
ComponentRegistry / ComponentSchema | Types for consuming the component registry JSON |
CLI
Kumo includes a CLI for querying component documentation and installing blocks:
# List all components grouped by category
npx @cloudflare/kumo ls
# Get detailed docs for a specific component
npx @cloudflare/kumo doc Button
npx @cloudflare/kumo doc Dialog
# Get all component documentation
npx @cloudflare/kumo docs
# List available blocks (composite page-level components)
npx @cloudflare/kumo blocks
# Install a block into your project
npx @cloudflare/kumo add <block-name>
# Initialize kumo.json configuration
npx @cloudflare/kumo init
# Migrate token names after breaking changes
npx @cloudflare/kumo migrate
Blocks
Blocks are composite, page-level components that are not part of the library bundle. They are installed directly into your project via the CLI:
npx @cloudflare/kumo add delete-resource
npx @cloudflare/kumo add page-header
npx @cloudflare/kumo add resource-list
After installation, block source files are copied into your project and can be customized freely.
Base UI Primitives
Kumo re-exports all Base UI primitives for advanced use cases where you need unstyled, accessible building blocks:
import { Accordion } from "@cloudflare/kumo/primitives/accordion";
import { AlertDialog } from "@cloudflare/kumo/primitives/alert-dialog";
import { ScrollArea } from "@cloudflare/kumo/primitives/scroll-area";
import { Slider } from "@cloudflare/kumo/primitives/slider";
import { NumberField } from "@cloudflare/kumo/primitives/number-field";
import { Progress } from "@cloudflare/kumo/primitives/progress";
Available primitives: accordion, alert-dialog, autocomplete, avatar, button, checkbox, checkbox-group, collapsible, combobox, context-menu, dialog, direction-provider, field, fieldset, form, input, menu, menubar, meter, navigation-menu, number-field, popover, preview-card, progress, radio, radio-group, scroll-area, select, separator, slider, switch, tabs, toast, toggle, toggle-group, toolbar, tooltip.
Semantic Token Reference
Kumo uses semantic color tokens that automatically adapt to light and dark mode. Use these instead of raw Tailwind colors:
| Token | Usage |
|---|---|
bg-kumo-base | Primary background surface |
bg-kumo-elevated | Elevated surface background |
bg-kumo-recessed | Recessed/inset background |
bg-kumo-control | Form control backgrounds (inputs, selects) |
bg-kumo-tint | Subtle tinted background |
bg-kumo-brand | Brand-colored background |
bg-kumo-danger | Danger/error background |
text-kumo-default | Primary text color |
text-kumo-subtle | Secondary/muted text |
text-kumo-strong | Emphasized text |
text-kumo-inverse | Inverse text (on dark backgrounds) |
text-kumo-link | Link text color |
text-kumo-danger | Error text |
border-kumo-line | Standard border color |
border-kumo-fill | Filled/heavier border |
ring-kumo-ring | Focus ring color |
ring-kumo-line | Border ring color |
bg-kumo-overlay | Overlay/backdrop background |
Allowed exceptions: bg-white, bg-black, text-white, text-black, transparent.