cloudflare-kumo

IndexedCommit: 36cdfe50 pullsUpdated Feb 13, 2026

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

Install this reference
Reference

@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

FilePurpose
packages/kumo/src/index.tsMain barrel export — all components, utils, types
packages/kumo/ai/component-registry.mdComplete component docs with props, variants, examples
packages/kumo/ai/component-registry.jsonMachine-readable component metadata (props, variants, colors)
README.mdInstallation and usage overview
packages/kumo/src/styles/kumo.cssKumo 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

  1. Always use semantic tokens — Use bg-kumo-base, text-kumo-default, border-kumo-line, ring-kumo-ring instead of raw Tailwind colors like bg-blue-500. Kumo's semantic tokens automatically adapt to light/dark mode.

  2. Never use the dark: variant — Dark mode is handled automatically via CSS light-dark() in the semantic token system. Adding dark: prefixes is redundant and may cause conflicts.

  3. Use cn() for className composition — Always merge custom classes with the cn() utility exported from @cloudflare/kumo to properly handle Tailwind class conflicts via tailwind-merge.

  4. Provide accessible names for inputs — All form components require an accessible name. Use the label prop (recommended), or aria-label/aria-labelledby for bare inputs. Development warnings will alert you to missing labels.

  5. Wrap your app with <Toasty> for toasts — The toast system requires a provider wrapping your application. Use the useKumoToastManager() hook in child components to create notifications.

  6. Use <LinkProvider> for framework routing — Wrap your app with LinkProvider to integrate Kumo's link-based components (Link, LinkButton, Breadcrumbs) with your router's <Link> component.

  7. Use tree-shakeable imports for smaller bundles — Import from granular paths like @cloudflare/kumo/components/button instead 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

ExportCategoryDescription
ButtonActionPrimary action trigger with variants: primary, secondary, ghost, destructive, outline
LinkButtonActionAnchor element styled as a button, integrates with LinkProvider
RefreshButtonActionPre-built refresh icon button with loading animation
InputInputText input with optional Field wrapper (label, description, error)
InputArea / TextareaInputMulti-line text input
InputGroupInputGroup multiple input-related elements
SelectInputDropdown select with Select.Option sub-component
CheckboxInputCheckbox with label support and group mode
SwitchInputToggle switch with size variants (sm, base, lg)
Radio / RadioGroupInputRadio button group
ComboboxInputAutocomplete/typeahead with single and multi-select support
DateRangePickerInputDate range selection component
SensitiveInputInputPassword/secret input with show/hide toggle
FieldInputForm field wrapper with label, description, and error display
DialogOverlayModal dialog with compound components: Root, Trigger, Title, Description, Close
DropdownMenuOverlayContext menu / dropdown with items, sub-menus, checkboxes
PopoverOverlayFloating content panel with trigger
Tooltip / TooltipProviderOverlayHover tooltip with positioning options
Toasty / ToastProviderFeedbackToast notification provider (wrap app root)
ToastFeedbackIndividual toast notification
BannerFeedbackFull-width message bar (default, alert, error)
Loader / SkeletonLineFeedbackAnimated spinner and skeleton loading line
BadgeDisplayStatus label (primary, secondary, destructive, outline, beta)
TextDisplayTypography component with heading and body variants
Code / CodeBlockDisplayInline code and code block display
MeterDisplayProgress/usage meter
EmptyDisplayEmpty state placeholder
LayerCardDisplayElevated card container
BreadcrumbsDisplayNavigation breadcrumb trail
CloudflareLogoDisplayCloudflare logo with variant and color options
TableDisplayData table with Header, Body, Row, Cell, CheckCell, Footer
TabsNavigationTab navigation (segmented or underline style)
PaginationNavigationPage navigation controls
MenuBarNavigationHorizontal menu bar
CommandPaletteNavigationSearchable command palette (⌘K pattern)
Grid / GridItemLayoutResponsive CSS grid with preset layouts (2up, 3up, 4up, etc.)
SurfaceLayoutPolymorphic container with background, shadow, and border
CollapsibleLayoutExpandable/collapsible content section
LinkNavigationStyled anchor that integrates with LinkProvider
ClipboardTextActionText with click-to-copy functionality
LabelInputAccessible label component

Utilities

ExportTypeDescription
cn(...classes)FunctionMerges Tailwind class names with conflict resolution (clsx + tailwind-merge)
safeRandomId()FunctionGenerates a unique ID using crypto.randomUUID when available
LinkProviderComponentContext provider for custom link components (router integration)
useLinkComponent()HookReturns the current link component from LinkProvider context
useKumoToastManager()HookReturns toast manager with add(), update(), promise() methods
buttonVariants()FunctionReturns computed Tailwind classes for button variant combinations
inputVariants()FunctionReturns computed Tailwind classes for input variant combinations

Types

ExportDescription
ButtonPropsButton props (discriminated union: text vs icon-only require aria-label)
LinkButtonPropsAnchor-styled button props with external option
InputPropsInput props with label, description, error for Field wrapper
DialogPropsDialog content panel props with size variant
TabsProps / TabsItemTabs configuration with tabs array and variant
BadgeVariantBadge variant union: "primary" | "secondary" | "destructive" | "outline" | "beta"
SelectPropsSelect dropdown props
CheckboxPropsCheckbox props with label and controlled/uncontrolled modes
LinkComponentPropsProps for custom link components used with LinkProvider
ComponentRegistry / ComponentSchemaTypes 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:

TokenUsage
bg-kumo-basePrimary background surface
bg-kumo-elevatedElevated surface background
bg-kumo-recessedRecessed/inset background
bg-kumo-controlForm control backgrounds (inputs, selects)
bg-kumo-tintSubtle tinted background
bg-kumo-brandBrand-colored background
bg-kumo-dangerDanger/error background
text-kumo-defaultPrimary text color
text-kumo-subtleSecondary/muted text
text-kumo-strongEmphasized text
text-kumo-inverseInverse text (on dark backgrounds)
text-kumo-linkLink text color
text-kumo-dangerError text
border-kumo-lineStandard border color
border-kumo-fillFilled/heavier border
ring-kumo-ringFocus ring color
ring-kumo-lineBorder ring color
bg-kumo-overlayOverlay/backdrop background

Allowed exceptions: bg-white, bg-black, text-white, text-black, transparent.

“Explore distant worlds.”

© 2026 Oscar Gabriel