React tutorial
Typed function components, one file each, controlled with di standard
value + onChange pattern — everything dey work di way React dey already work.
Wetin you go build
Small contact form — input wey get label, submit button, controlled state, real keyboard and screen-reader behaviour — first bare, then styled, for about 10 minutes.
Before you start
You need git, Node 22 or newer, and pnpm 10 or newer. Any React 18+ project dey work — Next.js, Vite, or plain React.
Step 1 — Get di code
git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-react-headless
pnpm install
pnpm test # vitest + @testing-library/react
pnpm run storybook # browse all 571 components
Di components na flat files for components/ —
Button.tsx, TextInput.tsx — each one with im test, story, and docs beside am. Copy di files wey you need enter your app, or vendor di repo and import from am.
Step 2 — Your first component
import Button from "lily-design-system-react-headless/components/Button";
export function SaveBar() {
return <Button onClick={save}>Save</Button>;
}
E dey render <button class="button" type="button">. Props:
type, disabled, pressed (e dey render
aria-pressed), label (an
aria-label override), className, and any extra attribute go spread onto di root element.
Step 3 — Compose small form
import { useState } from "react";
import Form from ".../components/Form";
import Field from ".../components/Field";
import TextInput from ".../components/TextInput";
import Button from ".../components/Button";
export function ContactForm() {
const [name, setName] = useState("");
return (
<Form label="Contact form" onSubmit={handleSubmit}>
<Field label="Full name" required>
<TextInput id="name" label="Full name" value={name} onChange={setName} required />
</Field>
<Button type="submit">Save</Button>
</Form>
);
}
Field dey take di visible label as im own prop instead of nested label component — pass am once, on
Field, and TextInputfor
label dey provide di accessible name.
Verified 2026-08-29: earlier draft nest separate Label inside Field, wey render
two labels (one empty) because Field already dey render im own from di label prop — dem catch am by compiling di exact snippet. Di
contact-form composed route
na di same pattern, wey real e2e and axe tests don exercise.
Step 4 — Style am
Everything above dey render as pure structure, ready for your styles. Style di hooks — .button,
.text-input, .label, .field — inside your stylesheet, layer Tailwind utilities through className, or link ready-made theme:
<link rel="stylesheet" href="/themes/united-states-web-design-system.css" />
Step 5 — See everything dey work
Di Next.js example app na di full catalog styled and demoed, with Playwright e2e and axe-core baseline:
git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-react-next-examples
pnpm install && pnpm run dev
Next steps
- Theming — runtime switching with di React theme-picker helper.
- Preference helpers — language and text size.
- Component catalog — props, ARIA, and keyboard for each component.
You get questions as you dey go? Di help page full of answers — and if any step fit clearer, tell us and we go gladly improve di tutorial.