new-component
Scaffold a new React component with optional Storybook story and Vitest test files. Use this skill whenever the user asks to "create a component", "make a button", "scaffold a card", "add a new compon
SKILL.mdfrontend/.claude/skills/new-component/SKILL.md
GitHubnew-component
Workflow
- Registry check first. Before writing any component by hand, check whether shadcn has it:
pnpm shadcn view <name>for a known item, orpnpm shadcn search(for examplepnpm shadcn search dialog) to look. A registry counterpart means a button, dialog, tabs, popover, select, table and the like. - If the registry has it, run
pnpm shadcn add <name>. The file lands flat inapp/components/ui/<name>.tsxand is vendored: do not edit it (seefrontend/.claude/rules/shadcn-ui.md). Then compose the added component in a GAIA component or page rather than hand-writing a replacement; addapp/components/ui/tests/<name>.stories.tsxfor the new ui file. Stop here unless the user still needs a GAIA component around it. - Only when the registry has no counterpart, or the request is app logic around ui parts: confirm with user via AskUserQuestion: name (PascalCase or kebab-case; the folder is kebab-case), parent dir (default
app/components), props (or "none"). - Run from the repo root:
./.gaia/cli/gaia scaffold component <Name> [flags](the package comes from the registry, not the working directory). - Verify:
pnpm typecheckclean. Open the new files, sanity-check the props. - If user wants more (variants, conditional rendering, complex children): edit the generated files. The skill does not regenerate. Before hand-editing, read one or two existing components with a similar shape and follow their pattern; style with role tokens and
cvafor variants (frontend/.claude/skills/tailwind/SKILL.md).
Adopter notes
- Registry hooks land in
~/hooksas kebab-caseuse-*.tsfiles, which GAIA's layout accepts. - The
utilsalias incomponents.jsonis required by the config schema but unused: registry files importcnfrom thecnpackage, soshadcn addwrites no util file.
Flags
--no-storyis refused (exit 1, nothing written): the story is the component's test, so a component cannot be scaffolded without one--parent <dir>, non-default parent dir, an existingapp/components[/...]folder (e.g.app/components/form) orapp/pages/<path>to colocate the component in a page folder. A parent equal to or underapp/components/ui, or outsideapp/componentsandapp/pages, is refused; the layout rule is infrontend/.claude/rules/coding-guidelines-react.md--props "a:string,b:number", typed props rendered as a Props alias and destructured in the signature. Only top-level commas separate props, so comma-bearing types (Record<K, V>,(a, b) => void, tuples) are supported within a single entry.
The story is the test
The scaffold writes tests/index.stories.tsx with a Default story, a Renders story and a play function on Renders, and no test file. Stories run as tests under @storybook/addon-vitest in headless Chromium (run pnpm install:browsers once), and addon-a11y axe-checks every story, so a story that renders a violation fails with no extra test. Import expect, within and userEvent from storybook/test.
With --props, the scaffolder fills representative values (title="title", count={0}, ...) at the story's render site. Replace the placeholders with realistic ones. The scaffolded play is a starting point, not complete evidence: add a story with a play per behavior (interactions, state transitions, error paths) as the component grows, and for an interactive component assert a keyboard path and a focus outcome. How to write plays: frontend/.claude/skills/tdd-react/references/tests-react.md.