The component playground

Find your
building blocks.

Try it here. Make it yours there.

48 components · React + CSSFirst time? Set up your theme →

Essentials

Button

The workhorse. 3px square border, inverted fill on primary, no shadow.

Code & usage

Example

import { useState } from 'react';
import { Button } from './ui/button/Button';

export function Actions() {
  const [action, setAction] = useState('');
  return (
    <div
      onClick={event => {
        const target = (event.target as Element).closest('button');
        if (target && !target.disabled) setAction(target.textContent ?? '');
      }}
      style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}
    >
      <Button variant='cta'>Start curriculum</Button>
      <Button variant='default'>Secondary</Button>
      <Button variant='danger'>Dangerous</Button>
      <Button variant='ghost'>Ghost</Button>
      <Button size='sm'>Small</Button>
      <Button size='lg'>Large</Button>
      <Button disabled>Disabled</Button>
      <p role='status' style={{ flexBasis: '100%', margin: 0 }}>
        {action && `Selected: ${action}`}
      </p>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/button/button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Badge

Inline status chip - sparingly. Max 2 per row.

DefaultPassedIn reviewFailed
Code & usage

Example

import { Badge } from './ui/badge/Badge';

export function Example() {
  return (
    <div
      style={{
        display: 'flex',
        flexWrap: 'wrap',
        alignItems: 'center',
        gap: 16
      }}
    >
      <Badge>Default</Badge>
      <Badge variant='success'>Passed</Badge>
      <Badge variant='warning'>In review</Badge>
      <Badge variant='danger'>Failed</Badge>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/badge/badge.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Close button

Inline dismiss affordance - modals, alerts, drawers.

Changes saved.
Code & usage

Example

import { Button } from './ui/button/Button';
import { useState } from 'react';
import { CloseButton } from './ui/close-button/CloseButton';

export function DismissibleNotice() {
  const [visible, setVisible] = useState(true);
  return visible ? (
    <div>
      Changes saved.{' '}
      <CloseButton
        onClick={() => setVisible(false)}
        aria-label='Dismiss notice'
      />
    </div>
  ) : (
    <Button onClick={() => setVisible(true)}>Show notice again</Button>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/close-button/close-button.css';
import './ui/button/button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Spacer

Explicit whitespace on an 8-step scale.

startend
Code & usage

Example

import { Spacer } from './ui/spacer/Spacer';

export function Example() {
  return (
    <div style={{ display: 'flex' }}>
      <span>start</span>
      <Spacer size={6} />
      <span>end</span>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/spacer/spacer.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Image

Responsive image wrapper with built-in aspect caption and alt enforcement.

freeCodeCamp mark
freeCodeCamp mark
Code & usage

Example

import { Image } from './ui/image/Image';

export function Example() {
  return (
    <Image
      style={{ background: '#f5f6f7', padding: 16 }}
      src='/brand/fcc-secondary.svg'
      alt='freeCodeCamp mark'
      caption='freeCodeCamp mark'
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/image/image.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Text

Body-text primitive - semantic-tag agnostic, size + weight + tone variants.

Large body - section ledes and emphasis.

Default body - eighteen pixels minimum.

Caption - annotations, metadata, footnotes.

Code & usage

Example

import { Text } from './ui/text/Text';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Text size='lg'>Large body - section ledes and emphasis.</Text>
      <Text>Default body - eighteen pixels minimum.</Text>
      <Text size='sm' tone='muted'>
        Caption - annotations, metadata, footnotes.
      </Text>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/text/text.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Heading

Headline primitive - independent of HTML level. Five visual sizes; tag chosen for outline.

Command-line Chic.

Ship interfaces.

Composable primitives.

Flat surfaces.

Square corners.
Code & usage

Example

import { Heading } from './ui/heading/Heading';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Heading level={1} size='display'>
        Command-line Chic.
      </Heading>
      <Heading level={2} size='xl'>
        Ship interfaces.
      </Heading>
      <Heading level={3} size='lg'>
        Composable primitives.
      </Heading>
      <Heading level={4} size='md'>
        Flat surfaces.
      </Heading>
      <Heading level={5} size='sm'>
        Square corners.
      </Heading>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/heading/heading.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Divider

Hairline rule. Solid or dashed; horizontal or vertical.

BeforeAfter
Code & usage

Example

import { Divider } from './ui/divider/Divider';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Divider />
      <Divider variant='dashed' />
      <div style={{ display: 'flex', alignItems: 'center', height: 48 }}>
        <span>Before</span>
        <Divider orientation='vertical' />
        <span>After</span>
      </div>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/divider/divider.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Avatar

User mark - image, initials fallback, optional online/away status dot.

Code & usage

Example

import { Avatar } from './ui/avatar/Avatar';

export function Example() {
  return (
    <div
      style={{
        display: 'flex',
        flexWrap: 'wrap',
        alignItems: 'center',
        gap: 16
      }}
    >
      <Avatar size='sm' name='Rosie Wilson' />
      <Avatar size='md' name='Quincy Larson' status='online' />
      <Avatar size='lg' name='Quincy Larson' status='away' />
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/avatar/avatar.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Forms

Input

Text input with a built-in label, helper text, and error slot.

We send one curriculum update per week.

Code & usage

Example

import { FormGroup } from './ui/form-group/FormGroup';
import { Input } from './ui/input/Input';
import { HelpBlock } from './ui/help-block/HelpBlock';

export function Example() {
  return (
    <FormGroup>
      <label htmlFor='email'>Email address</label>
      <Input id='email' type='email' placeholder='camper@example.com' />
      <HelpBlock>We send one curriculum update per week.</HelpBlock>
    </FormGroup>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/input/input.css';
import './ui/form-group/form-group.css';
import './ui/help-block/help-block.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Checkbox

Flat, square, accent-filled checkmark.

Code & usage

Example

import { Checkbox } from './ui/checkbox/Checkbox';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Checkbox defaultChecked label='I accept the honor code' />
      <Checkbox label='Email me certificate alerts' />
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/checkbox/checkbox.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Toggle button

For stateful binary choices - hours pressed, unit selected, dark mode on.

Code & usage

Example

import { useState } from 'react';
import { ToggleButton } from './ui/toggle-button/ToggleButton';
export function Pressed() {
  const [on, setOn] = useState(false);
  return (
    <ToggleButton pressed={on} onPressedChange={setOn}>
      {on ? 'On' : 'Off'}
    </ToggleButton>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/toggle-button/toggle-button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Switch

For settings that take effect immediately - theme, audio, keybinds.

Code & usage

Example

import { Switch } from './ui/switch/Switch';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Switch defaultChecked label='Keyboard shortcuts' />
      <Switch label='Sound effects' />
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/switch/switch.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Form group

Groups a label, control, and help text with consistent spacing.

Letters, numbers, and dashes. Public.

Code & usage

Example

import { FormGroup } from './ui/form-group/FormGroup';
import { Input } from './ui/input/Input';
import { HelpBlock } from './ui/help-block/HelpBlock';

export function Example() {
  return (
    <FormGroup>
      <label htmlFor='username'>Username</label>
      <Input id='username' defaultValue='camper-42' />
      <HelpBlock>Letters, numbers, and dashes. Public.</HelpBlock>
    </FormGroup>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/form-group/form-group.css';
import './ui/input/input.css';
import './ui/help-block/help-block.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Form control

Low-level text control - no label wrapper, for custom layouts.

Code & usage

Example

import { FormGroup } from './ui/form-group/FormGroup';
import { FormControl } from './ui/form-control/FormControl';

export function Example() {
  return (
    <FormGroup>
      <label htmlFor='curriculum-search'>Search curriculum</label>
      <FormControl id='curriculum-search' placeholder='Search the curriculum' />
    </FormGroup>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/form-control/form-control.css';
import './ui/input/input.css';
import './ui/form-group/form-group.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Help block

Field-level guidance - neutral, success, or error tone.

We send one curriculum update per week.

Username available.

Username already in use.

Code & usage

Example

import { HelpBlock } from './ui/help-block/HelpBlock';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <HelpBlock>We send one curriculum update per week.</HelpBlock>
      <HelpBlock variant='success'>Username available.</HelpBlock>
      <HelpBlock variant='error'>Username already in use.</HelpBlock>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/help-block/help-block.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Fieldset

Groups related controls under a legend - required for radio groups.

Notification cadence
Code & usage

Example

import { Fieldset } from './ui/fieldset/Fieldset';
import { RadioGroup, Radio } from './ui/radio/Radio';

export function Example() {
  return (
    <Fieldset legend='Notification cadence'>
      <RadioGroup
        aria-label='Notification cadence'
        name='cadence'
        defaultValue='weekly'
      >
        <Radio value='weekly' label='Weekly digest' />
        <Radio value='per-cert' label='Per-cert' />
        <Radio value='never' label='Never' />
      </RadioGroup>
    </Fieldset>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/fieldset/fieldset.css';
import './ui/radio/radio.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Radio

Mutually exclusive choice - group with `name` or `radio-group`.

Code & usage

Example

import { Radio, RadioGroup } from './ui/radio/Radio';

export function Example() {
  return (
    <RadioGroup name='theme' defaultValue='dark' aria-label='Theme'>
      <Radio value='dark' label='Dark - default' />
      <Radio value='light' label='Light' />
      <Radio value='system' label='System' />
    </RadioGroup>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/radio/radio.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Select

Native select wrapper - square chevron, matching height with Input.

Code & usage

Example

import { FormGroup } from './ui/form-group/FormGroup';
import { Select } from './ui/select/Select';

export function Example() {
  return (
    <FormGroup>
      <label htmlFor='difficulty'>Difficulty</label>
      <Select id='difficulty' defaultValue='intermediate'>
        <option value='beginner'>Beginner</option>
        <option value='intermediate'>Intermediate</option>
        <option value='advanced'>Advanced</option>
      </Select>
    </FormGroup>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/select/select.css';
import './ui/form-group/form-group.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Textarea

Multi-line text control with mono variant for code snippets.

Code & usage

Example

import { FormGroup } from './ui/form-group/FormGroup';
import { Textarea } from './ui/textarea/Textarea';

export function Example() {
  return (
    <FormGroup>
      <label htmlFor='bio'>Bio</label>
      <Textarea
        id='bio'
        rows={3}
        placeholder='What are you learning right now?'
      />
    </FormGroup>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/textarea/textarea.css';
import './ui/form-group/form-group.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Form stepper

Multi-step form progress - keyboard navigable.

Create your account.

Code & usage

Example

import { FormStepper } from './ui/form-stepper/FormStepper';
import { Button } from './ui/button/Button';
import { useState } from 'react';

const steps = [
  { id: 'account', label: 'Account', description: 'Email + handle' },
  { id: 'goals', label: 'Goals', description: 'What to learn first' },
  { id: 'confirm', label: 'Confirm', description: 'Review + start' }
];
const content = [
  'Create your account.',
  'Choose your learning goals.',
  'Review your choices.'
];

export function Example() {
  const [index, setIndex] = useState(0);
  return (
    <FormStepper
      steps={steps}
      current={steps[index]!.id}
      onStepChange={id => setIndex(steps.findIndex(step => step.id === id))}
    >
      {() => (
        <div>
          <p role='status'>{content[index]}</p>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
            <Button disabled={index === 0} onClick={() => setIndex(index - 1)}>
              Back
            </Button>
            <Button
              variant='cta'
              onClick={() =>
                setIndex(index === steps.length - 1 ? 0 : index + 1)
              }
            >
              {index === steps.length - 1 ? 'Start again' : 'Next'}
            </Button>
          </div>
        </div>
      )}
    </FormStepper>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/form-stepper/form-stepper.css';
import './ui/button/button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Navigation

Tabs

Inverted-fill selected state, matches the platform editor panel.

Build a page with a heading and a paragraph.
Code & usage

Example

import { Tabs, Tab } from './ui/tabs/Tabs';

export function Example() {
  return (
    <Tabs defaultActiveKey='instructions'>
      <Tab eventKey='instructions' title='Instructions'>
        Build a page with a heading and a paragraph.
      </Tab>
      <Tab eventKey='tests' title='Tests'>
        Your heading and paragraph tests pass.
      </Tab>
      <Tab eventKey='console' title='Console'>
        Ready. Run your code to see its output.
      </Tab>
    </Tabs>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/tabs/tabs.css';

Requires react@>=18 <20, @ark-ui/react@^5.0.0. Interaction guidance ↗

Read the complete Markdown reference →

Pagination

Page selector - Ark UI pagination machine, keyboard navigable.

Code & usage

Example

import { useState } from 'react';
import { Pagination } from './ui/pagination/Pagination';

export function Example() {
  const [page, setPage] = useState(2);

  return (
    <Pagination
      count={120}
      pageSize={10}
      page={page}
      onPageChange={page => setPage(page)}
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/pagination/pagination.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Listbox

Persistent selection list - keyboard navigable, single or multi-select.

  • Frontend
  • Backend
Code & usage

Example

import { Listbox } from './ui/listbox/Listbox';
import { useState } from 'react';

export function Example() {
  const ITEMS = [
    { value: 'frontend', label: 'Frontend' },
    { value: 'backend', label: 'Backend' }
  ];

  const [value, setValue] = useState<string | string[]>('frontend');

  return (
    <Listbox
      aria-label='Learning track'
      items={ITEMS}
      value={value}
      onValueChange={setValue}
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/listbox/listbox.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Combobox

Typeahead select. Filters as you type.

Code & usage

Example

import { Combobox, filterItemsByLabel } from './ui/combobox/Combobox';
import { useState } from 'react';

export function Example() {
  const ALL = [
    { value: 'rwd', label: 'Responsive Web Design' },
    { value: 'js', label: 'JavaScript Algorithms' }
  ];

  const [query, setQuery] = useState('');
  const [value, setValue] = useState<string | null>(null);
  const items = filterItemsByLabel(ALL, query);

  return (
    <Combobox
      inputValue={query}
      onInputValueChange={setQuery}
      value={value}
      onValueChange={setValue}
      items={items}
      aria-label='Certification'
      placeholder='Pick a certification'
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/combobox/combobox.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Overlays

Tooltip

Contextual hint on hover or focus - keep to 80 chars.

Runs the public test suite against your code.
Code & usage

Example

import { useState } from 'react';
import { Tooltip } from './ui/tooltip/Tooltip';
import { Button } from './ui/button/Button';

export function Example() {
  const [complete, setComplete] = useState(false);
  return (
    <div style={{ display: 'grid', gap: 16 }}>
      <Tooltip content='Runs the public test suite against your code.'>
        <Button onClick={() => setComplete(true)}>Run tests</Button>
      </Tooltip>
      {complete && <p role='status'>Example tests passed.</p>}
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/tooltip/tooltip.css';
import './ui/button/button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Toast

Transient status surface - Ark UI toaster machine, reduced-motion aware.

Code & usage

Example

import { Button } from './ui/button/Button';
import { Toaster, createToaster } from './ui/toast/Toast';
const toaster = createToaster({});
export function SaveNotice() {
  return (
    <>
      <Button
        onClick={() => toaster.create({ title: 'Saved', type: 'success' })}
      >
        Save
      </Button>
      <Toaster toaster={toaster} />
    </>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/toast/toast.css';
import './ui/button/button.css';

Requires react@>=18 <20, @ark-ui/react@^5.0.0. Interaction guidance ↗

Read the complete Markdown reference →

Command palette

⌘K spotlight - global commands, grouped, keyboard-shortcut hinted.

Code & usage

Example

import { Button } from './ui/button/Button';
import { useState } from 'react';
import { CommandPalette } from './ui/command-palette/CommandPalette';

export function Example() {
  const GROUPS = [
    {
      label: 'Navigation',
      items: [
        { id: 'curriculum', label: 'Go to curriculum', shortcut: 'G C' },
        { id: 'settings', label: 'Open settings', shortcut: 'G S' }
      ]
    }
  ];

  const [open, setOpen] = useState(false);
  const [selected, setSelected] = useState('');

  return (
    <>
      <Button onClick={() => setOpen(true)}>Open commands</Button>
      <p role='status'>{selected && `Selected: ${selected}`}</p>
      <CommandPalette
        open={open}
        onClose={() => setOpen(false)}
        onSelect={id => {
          setSelected(id);
          setOpen(false);
        }}
        groups={GROUPS}
        placeholder='Type a command or search…'
      />
    </>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/command-palette/command-palette.css';
import './ui/button/button.css';

Requires react@>=18 <20, @ark-ui/react@^5.0.0. Interaction guidance ↗

Read the complete Markdown reference →

Data & feedback

Card

Flat bordered container. Grid-friendly. No shadow, no hover lift.

300 HOURS

Responsive Web Design

Build five certification projects...

Code & usage

Example

import { Card } from './ui/card/Card';
import { Link } from './ui/link/Link';

export function Example() {
  return (
    <Card>
      <Card.Header>
        <span className='card__dot card__dot--purple' aria-hidden='true' />
        <p className='card__hours'>300 HOURS</p>
      </Card.Header>
      <Card.Title>Responsive Web Design</Card.Title>
      <Card.Body>Build five certification projects...</Card.Body>
      <Card.Footer>
        <span>62% complete</span>
        <Link href='https://www.freecodecamp.org/learn/2022/responsive-web-design/'>
          Resume →
        </Link>
      </Card.Footer>
    </Card>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/card/card.css';
import './ui/link/link.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Panel

Borderless surface-two container - sidebars, inspectors, nested regions.

Editor hints

Your code runs against the first test each time you save.
Code & usage

Example

import { Panel } from './ui/panel/Panel';

export function Example() {
  return (
    <Panel title='Editor hints'>
      Your code runs against the first test each time you save.
    </Panel>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/panel/panel.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Alert

Full-width status surface for page-level state - saved, failed, queued.

Code & usage

Example

import { Alert } from './ui/alert/Alert';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Alert variant='success'>
        All 28 tests pass. Next challenge unlocked.
      </Alert>
      <Alert variant='warning'>You have one unsaved edit.</Alert>
      <Alert variant='danger'>Sign-in failed - check your email address.</Alert>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/alert/alert.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Callout

Inline sidebar for curriculum notes, tips, and cautions.

Code & usage

Example

import { Callout } from './ui/callout/Callout';

export function Example() {
  return (
    <Callout variant='tip' label='Tip'>
      Open the editor fullscreen with <kbd>F11</kbd>.
    </Callout>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/callout/callout.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Table

Rule-heavy, square, mono-numeric - for progress and diagnostic data.

CertificationProjectsStatus
Responsive Web Design5 / 5Passed
Code & usage

Example

import { Table } from './ui/table/Table';
import { Badge } from './ui/badge/Badge';

export function Example() {
  return (
    <div
      role='region'
      aria-label='Certification progress'
      tabIndex={0}
      style={{ width: '100%', overflowX: 'auto' }}
    >
      <Table>
        <thead>
          <tr>
            <th>Certification</th>
            <th>Projects</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Responsive Web Design</td>
            <td>5 / 5</td>
            <td>
              <Badge variant='success'>Passed</Badge>
            </td>
          </tr>
        </tbody>
      </Table>
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/table/table.css';
import './ui/badge/badge.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Description list

Term + detail metadata pair - stacked or inline layout.

Username
camper-42
Joined
2014-04-12
Certifications
3 of 14
Code & usage

Example

import { DescriptionList } from './ui/description-list/DescriptionList';

export function Example() {
  return (
    <DescriptionList
      items={[
        { term: 'Username', detail: 'camper-42' },
        { term: 'Joined', detail: '2014-04-12' },
        { term: 'Certifications', detail: '3 of 14' }
      ]}
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/description-list/description-list.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Skeleton

Placeholder bars during data fetch - reduced-motion aware shimmer.

Code & usage

Example

import { Skeleton } from './ui/skeleton/Skeleton';

export function Example() {
  return (
    <div style={{ display: 'grid', gap: 16, width: '100%' }}>
      <Skeleton variant='circle' width={48} height={48} />
      <Skeleton variant='text' width='80%' />
      <Skeleton variant='text' width='60%' />
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/skeleton/skeleton.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Empty state

No-data placeholder with title, description, and a primary action.

No learning goals yet

Add a goal to start planning your next project.

Code & usage

Example

import { useState } from 'react';
import { EmptyState } from './ui/empty-state/EmptyState';
import { Button } from './ui/button/Button';

export function Example() {
  const [started, setStarted] = useState(false);
  if (started)
    return (
      <div>
        <p role='status'>Your first learning goal is ready.</p>
        <Button onClick={() => setStarted(false)}>Reset example</Button>
      </div>
    );
  return (
    <EmptyState
      title='No learning goals yet'
      description='Add a goal to start planning your next project.'
      action={
        <Button variant='cta' onClick={() => setStarted(true)}>
          Add learning goal
        </Button>
      }
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/empty-state/empty-state.css';
import './ui/button/button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Data table

Sortable, selectable, paginated table - header sort buttons, optional row select.

Hours
Responsive Web Design300
JavaScript300
Code & usage

Example

import { DataTable, type DataTableSort } from './ui/data-table/DataTable';
import { useState } from 'react';

const rows = [
  { id: 'rwd', cert: 'Responsive Web Design', hours: 300 },
  { id: 'js', cert: 'JavaScript', hours: 300 }
];

export function Certifications() {
  const [sortBy, setSortBy] = useState<DataTableSort | null>(null);
  const sorted = [...rows].sort((a, b) =>
    sortBy
      ? a.cert.localeCompare(b.cert) * (sortBy.direction === 'asc' ? 1 : -1)
      : 0
  );
  return (
    <DataTable
      columns={[
        {
          id: 'cert',
          accessor: 'cert',
          header: 'Certification',
          sortable: true
        },
        { id: 'hours', accessor: 'hours', header: 'Hours', align: 'right' }
      ]}
      rows={sorted}
      sortBy={sortBy}
      onSortChange={setSortBy}
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/data-table/data-table.css';
import './ui/skeleton/skeleton.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Layouts

Stacked layout

Header + main + footer - the marketing/news chrome shape.

Curriculum

Code & usage

Example

import { StackedLayout } from './ui/stacked-layout/StackedLayout';
import { Navbar } from './ui/navbar/Navbar';

export function Example() {
  return (
    <StackedLayout
      header={<Navbar start='freeCodeCamp' />}
      footer={<footer>…</footer>}
    >
      <h1>Curriculum</h1>
    </StackedLayout>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/stacked-layout/stacked-layout.css';
import './ui/navbar/navbar.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Auth layout

Centered card on a tile-pattern backdrop - sign-in, sign-up, password reset.

freeCodeCamp

Code & usage

Example

import { useState } from 'react';
import { Link } from './ui/link/Link';
import { AuthLayout } from './ui/auth-layout/AuthLayout';
import { Input } from './ui/input/Input';
import { FormGroup } from './ui/form-group/FormGroup';
import { Button } from './ui/button/Button';

export function Example() {
  const [message, setMessage] = useState('');
  return (
    <AuthLayout
      pattern
      brand='freeCodeCamp'
      footer={
        <Link href='https://www.freecodecamp.org/signin'>
          Sign in to freeCodeCamp
        </Link>
      }
    >
      <form
        onSubmit={event => {
          event.preventDefault();
          const fields = new FormData(event.currentTarget);
          setMessage(`Ready to continue with ${fields.get('email')}.`);
        }}
      >
        <FormGroup>
          <label htmlFor='auth-email'>Email</label>
          <Input
            id='auth-email'
            name='email'
            type='email'
            autoComplete='email'
            required
          />
        </FormGroup>
        <Button type='submit' variant='cta' block>
          Continue
        </Button>
        <p role='status' style={{ margin: message ? '16px 0 0' : 0 }}>
          {message}
        </p>
      </form>
    </AuthLayout>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/auth-layout/auth-layout.css';
import './ui/link/link.css';
import './ui/input/input.css';
import './ui/form-group/form-group.css';
import './ui/button/button.css';

Requires react@>=18 <20. Interaction guidance ↗

Read the complete Markdown reference →

Learning

Tile matcher

Data-driven memory / matching game for interactive curricula. Flip to reveal, match pairs, fire callbacks.

Code & usage

Example

import { TileMatcher, type TileMatcherPair } from './ui/tile-matcher/TileMatcher';

const pairs: TileMatcherPair[] = [
  { id: 'html', faces: ['HTML', 'Structure'] },
  { id: 'css', faces: ['CSS', 'Style'] },
  { id: 'js', faces: ['JS', 'Behavior'] }
];

export function Drill() {
  return (
    <TileMatcher
      pairs={pairs}
      columns={3}
      seed={1}
      onMatch={id => console.log('matched', id)}
      onComplete={({ moves }) => console.log('done in', moves, 'moves')}
    />
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/tile-matcher/tile-matcher.css';

Requires react@>=18 <20.

Read the complete Markdown reference →

Hotspots

Clickable regions overlaid on a background image or diagram. Quiz mode with target, feedback, and hints.

Click the ellipse

Code & usage

Example

import { Hotspots, type HotspotItem } from './ui/hotspots/Hotspots';
import { RectHotspot, EllipseHotspot } from './ui/hotspot-shapes/HotspotShapes';

const Diagram = () => (
  <svg viewBox='0 0 200 140' role='img' aria-label='Three shapes'>
    <rect x='33' y='25' width='29' height='92' fill='currentColor' />
    <ellipse cx='100' cy='75' rx='30' ry='45' fill='currentColor' />
    <rect x='138' y='25' width='29' height='92' fill='currentColor' />
  </svg>
);

const HOTSPOTS: HotspotItem[] = [
  {
    id: 'bracket-left',
    label: 'Opening Paren',
    shape: <RectHotspot x={33} y={25} width={29} height={92} />
  },
  {
    id: 'fire',
    label: 'Ellipse',
    shape: <EllipseHotspot cx={100} cy={75} rx={30} ry={45} />
  },
  {
    id: 'bracket-right',
    label: 'Closing Paren',
    shape: <RectHotspot x={138} y={25} width={29} height={92} />
  }
];

export function HotspotsDemo() {
  return (
    <div style={{ width: '100%', maxWidth: 360, margin: '0 auto' }}>
      <Hotspots
        background={<Diagram />}
        width={200}
        height={140}
        hotspots={HOTSPOTS}
        targetId='fire'
        prompt='Click the ellipse'
        onCorrect={id => console.log('correct', id)}
      />
    </div>
  );
}

Files to copy

Copy these files into your project. Import the CSS once, after the shared theme.

CSS imports

Add these imports once in your entry file in src/.

import './ui/theme/tokens.css';
import './ui/theme/base.css';
import './ui/hotspots/hotspots.css';

Requires react@>=18 <20.

Read the complete Markdown reference →