Overview
Creates complete, production-grade React functional components using TypeScript. The skill outputs a component with a strict Props interface (using interface or type), sensible default props via destructuring, full JSDoc, proper forwardRef when needed, React.memo guidance, data-testid attributes for testing, a basic Storybook story stub, and accessibility considerations baked in.
When to Use This Skill
- Scaffolding a new UI component in a React + TypeScript project.
- The user provides a component name, list of props, behavior description, or a design spec/wireframe.
- You need consistent, high-quality component boilerplate that follows team conventions (props naming, JSDoc, testing attributes).
- Starting a component library, design system, or feature with many similar components.
Prerequisites
- Existing React project (Next.js, Vite, CRA, or custom) using TypeScript.
- Component library conventions already established (or the skill will propose sensible defaults).
react and @types/react installed (version 18+ recommended).
- For Storybook stories: Storybook 7+ or 8+ configured in the project.
- Optional but recommended:
clsx or tailwind-merge for className merging if using Tailwind.
Steps
Gather component specification:
- Component name (PascalCase).
- List of props with types and whether they are required.
- Behavioral description (what it does, variants, states).
- Accessibility requirements.
- Whether it needs
ref forwarding, children, or complex state.
- Styling approach (Tailwind, CSS Modules, styled-components, plain CSS).
Define the Props interface:
- Use
interface ComponentNameProps { ... } (preferred for declaration merging).
- Make optional props explicit with
?.
- Use union types for variants (e.g.,
variant?: 'primary' | 'secondary' | 'danger').
- Include
className?: string and children?: React.ReactNode where appropriate.
- Add JSDoc to every prop.
Implement the component function:
- Use arrow function with explicit return type
React.FC<ComponentNameProps> or just the function for better tree-shaking.
- Destructure props with defaults:
const { variant = 'primary', ... } = props;.
- Apply
React.forwardRef when the component renders a DOM element that should expose a ref (buttons, inputs, etc.).
- Use
React.memo only when the component is pure and props are frequently stable (document the decision).
Add accessibility and ARIA:
- Include
aria-* attributes based on role and state.
- Use semantic HTML elements inside the component.
- Add
role only when necessary (avoid over-using).
Include testing helpers:
- Add
data-testid={testId || 'component-name'} on the root or key interactive elements.
- Suggest a basic test file structure using React Testing Library.
Generate a Storybook story stub:
- Create a
.stories.tsx file with the component imported.
- Include controls for all variants and states.
- Add a "Playground" story and 2-3 specific stories (e.g., "Primary", "Disabled", "WithIcon").
Handle styling:
- If Tailwind: use
clsx or template literals with conditional classes.
- Provide a
cn utility example if the project doesn't have one.
- Keep style logic inside the component or extract to a separate
variants.ts when complex.
Add JSDoc and displayName:
- Top-level JSDoc describing the component purpose and usage.
Component.displayName = 'ComponentName'; for better debugging in React DevTools.
Output files:
- The main component file (
ComponentName.tsx).
- The story file (
ComponentName.stories.tsx).
- Optional: a basic test file skeleton.
- Usage example in the skill response.
Recommend follow-up:
- If form-related, suggest
form-validator.
- If animation needed, suggest
css-animation-crafter.
Examples
Example 1: Button Component
User request: "Create a Button component in React + TS. Variants: primary, secondary, ghost, danger. Sizes: sm, md, lg. Supports disabled, loading, left/right icon, and full width. Use Tailwind."
Generated Component (abbreviated for brevity in this example; full in real output):
import React from 'react';
import { clsx } from 'clsx';
export interface ButtonProps {
/** Visual style variant */
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
/** Size variant */
size?: 'sm' | 'md' | 'lg';
/** Whether the button is in a loading state */
isLoading?: boolean;
/** Left icon element */
leftIcon?: React.ReactNode;
/** Right icon element */
rightIcon?: React.ReactNode;
/** Make button take full width of container */
fullWidth?: boolean;
/** Disable the button */
disabled?: boolean;
children: React.ReactNode;
className?: string;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
}
/**
* A versatile, accessible button component with multiple variants and states.
*
* @example
* <Button variant="primary" size="md">Click me</Button>
*/
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(
{
variant = 'primary',
size = 'md',
isLoading = false,
leftIcon,
rightIcon,
fullWidth = false,
disabled,
children,
className,
...props
},
ref
) => {
const baseStyles = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none';
const variantStyles = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500',
ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',
danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
};
const sizeStyles = {
sm: 'px-3 py-1.5 text-sm gap-1.5',
md: 'px-4 py-2 text-base gap-2',
lg: 'px-6 py-3 text-lg gap-2.5',
};
return (
<button
ref={ref}
type="button"
disabled={disabled || isLoading}
className={clsx(
baseStyles,
variantStyles[variant],
sizeStyles[size],
fullWidth && 'w-full',
className
)}
aria-busy={isLoading}
data-testid="button"
{...props}
>
{isLoading && <span className="animate-spin mr-2">⏳</span>}
{leftIcon && <span className="mr-2">{leftIcon}</span>}
{children}
{rightIcon && <span className="ml-2">{rightIcon}</span>}
</button>
);
}
);
Button.displayName = 'Button';
Storybook stub also generated with all controls.
Example 2: Card with children and ref forwarding (similar detailed output).
Edge Cases & Error Handling
- Polymorphic component (as="a" or as="div"): Provide an
as prop using a generic or asChild pattern with Radix-style slot if the project uses it. Otherwise, keep it simple with a Component variable.
- Complex children: Document when to use
children vs dedicated props (e.g., title and description for a card).
- Performance: If the component renders lists or has expensive children, document when to wrap with
React.memo and provide a custom areEqual example.
- TypeScript strictness: Always use
strict mode friendly types. Avoid any. Use React.ComponentPropsWithoutRef<'button'> for native props spreading when appropriate.
- Icon libraries: Show how to accept
React.ReactNode for icons so any icon library works (Heroicons, Lucide, etc.).
- Ref forwarding with generics: Provide the correct generic signature for
forwardRef.
Verification
- Create the files in the project.
- Run
npm run build or tsc --noEmit — zero TypeScript errors.
- Import and render the component in a page or Storybook. Verify it matches the spec.
- Open React DevTools — confirm
displayName is set and props look clean.
- Run Storybook and interact with all controls.
- Write a quick test using the
data-testid and run it with your test runner.
- Check accessibility with axe or Lighthouse on a story that exercises all states.
- Success: Component is typed correctly, renders without warnings, passes tests, and is easy for other developers to use and extend.
References
1---2name: react-component-generator3description: Generates production-ready React functional components with TypeScript, props interface, default props, and JSDoc. Use when scaffolding new React components from descriptions or design specs.4license: Apache-2.05compatibility: Claude Code, Codex, Gemini CLI6metadata:7 author: ai-skills8 version: "1.0"9 category: web-development10 tags: react, typescript, components, functional, props, storybook11---1213## Overview1415Creates complete, production-grade React functional components using TypeScript. The skill outputs a component with a strict `Props` interface (using `interface` or `type`), sensible default props via destructuring, full JSDoc, proper `forwardRef` when needed, `React.memo` guidance, `data-testid` attributes for testing, a basic Storybook story stub, and accessibility considerations baked in.1617## When to Use This Skill1819- Scaffolding a new UI component in a React + TypeScript project.20- The user provides a component name, list of props, behavior description, or a design spec/wireframe.21- You need consistent, high-quality component boilerplate that follows team conventions (props naming, JSDoc, testing attributes).22- Starting a component library, design system, or feature with many similar components.2324## Prerequisites2526- Existing React project (Next.js, Vite, CRA, or custom) using TypeScript.27- Component library conventions already established (or the skill will propose sensible defaults).28- `react` and `@types/react` installed (version 18+ recommended).29- For Storybook stories: Storybook 7+ or 8+ configured in the project.30- Optional but recommended: `clsx` or `tailwind-merge` for className merging if using Tailwind.3132## Steps33341. **Gather component specification**:35 - Component name (PascalCase).36 - List of props with types and whether they are required.37 - Behavioral description (what it does, variants, states).38 - Accessibility requirements.39 - Whether it needs `ref` forwarding, children, or complex state.40 - Styling approach (Tailwind, CSS Modules, styled-components, plain CSS).41422. **Define the Props interface**:43 - Use `interface ComponentNameProps { ... }` (preferred for declaration merging).44 - Make optional props explicit with `?`.45 - Use union types for variants (e.g., `variant?: 'primary' | 'secondary' | 'danger'`).46 - Include `className?: string` and `children?: React.ReactNode` where appropriate.47 - Add JSDoc to every prop.48493. **Implement the component function**:50 - Use arrow function with explicit return type `React.FC<ComponentNameProps>` or just the function for better tree-shaking.51 - Destructure props with defaults: `const { variant = 'primary', ... } = props;`.52 - Apply `React.forwardRef` when the component renders a DOM element that should expose a ref (buttons, inputs, etc.).53 - Use `React.memo` only when the component is pure and props are frequently stable (document the decision).54554. **Add accessibility and ARIA**:56 - Include `aria-*` attributes based on role and state.57 - Use semantic HTML elements inside the component.58 - Add `role` only when necessary (avoid over-using).59605. **Include testing helpers**:61 - Add `data-testid={testId || 'component-name'}` on the root or key interactive elements.62 - Suggest a basic test file structure using React Testing Library.63646. **Generate a Storybook story stub**:65 - Create a `.stories.tsx` file with the component imported.66 - Include controls for all variants and states.67 - Add a "Playground" story and 2-3 specific stories (e.g., "Primary", "Disabled", "WithIcon").68697. **Handle styling**:70 - If Tailwind: use `clsx` or template literals with conditional classes.71 - Provide a `cn` utility example if the project doesn't have one.72 - Keep style logic inside the component or extract to a separate `variants.ts` when complex.73748. **Add JSDoc and displayName**:75 - Top-level JSDoc describing the component purpose and usage.76 - `Component.displayName = 'ComponentName';` for better debugging in React DevTools.77789. **Output files**:79 - The main component file (`ComponentName.tsx`).80 - The story file (`ComponentName.stories.tsx`).81 - Optional: a basic test file skeleton.82 - Usage example in the skill response.838410. **Recommend follow-up**:85 - If form-related, suggest `form-validator`.86 - If animation needed, suggest `css-animation-crafter`.8788## Examples8990**Example 1: Button Component**9192**User request**: "Create a Button component in React + TS. Variants: primary, secondary, ghost, danger. Sizes: sm, md, lg. Supports disabled, loading, left/right icon, and full width. Use Tailwind."9394**Generated Component** (abbreviated for brevity in this example; full in real output):9596```tsx97import React from 'react';98import { clsx } from 'clsx';99100export interface ButtonProps {101 /** Visual style variant */102 variant?: 'primary' | 'secondary' | 'ghost' | 'danger';103 /** Size variant */104 size?: 'sm' | 'md' | 'lg';105 /** Whether the button is in a loading state */106 isLoading?: boolean;107 /** Left icon element */108 leftIcon?: React.ReactNode;109 /** Right icon element */110 rightIcon?: React.ReactNode;111 /** Make button take full width of container */112 fullWidth?: boolean;113 /** Disable the button */114 disabled?: boolean;115 children: React.ReactNode;116 className?: string;117 onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;118}119120/**121 * A versatile, accessible button component with multiple variants and states.122 * 123 * @example124 * <Button variant="primary" size="md">Click me</Button>125 */126export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(127 (128 {129 variant = 'primary',130 size = 'md',131 isLoading = false,132 leftIcon,133 rightIcon,134 fullWidth = false,135 disabled,136 children,137 className,138 ...props139 },140 ref141 ) => {142 const baseStyles = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none';143 144 const variantStyles = {145 primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',146 secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500',147 ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',148 danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',149 };150151 const sizeStyles = {152 sm: 'px-3 py-1.5 text-sm gap-1.5',153 md: 'px-4 py-2 text-base gap-2',154 lg: 'px-6 py-3 text-lg gap-2.5',155 };156157 return (158 <button159 ref={ref}160 type="button"161 disabled={disabled || isLoading}162 className={clsx(163 baseStyles,164 variantStyles[variant],165 sizeStyles[size],166 fullWidth && 'w-full',167 className168 )}169 aria-busy={isLoading}170 data-testid="button"171 {...props}172 >173 {isLoading && <span className="animate-spin mr-2">⏳</span>}174 {leftIcon && <span className="mr-2">{leftIcon}</span>}175 {children}176 {rightIcon && <span className="ml-2">{rightIcon}</span>}177 </button>178 );179 }180);181182Button.displayName = 'Button';183```184185**Storybook stub** also generated with all controls.186187**Example 2: Card with children and ref forwarding** (similar detailed output).188189## Edge Cases & Error Handling190191- **Polymorphic component** (as="a" or as="div"): Provide an `as` prop using a generic or `asChild` pattern with Radix-style slot if the project uses it. Otherwise, keep it simple with a `Component` variable.192- **Complex children**: Document when to use `children` vs dedicated props (e.g., `title` and `description` for a card).193- **Performance**: If the component renders lists or has expensive children, document when to wrap with `React.memo` and provide a custom `areEqual` example.194- **TypeScript strictness**: Always use `strict` mode friendly types. Avoid `any`. Use `React.ComponentPropsWithoutRef<'button'>` for native props spreading when appropriate.195- **Icon libraries**: Show how to accept `React.ReactNode` for icons so any icon library works (Heroicons, Lucide, etc.).196- **Ref forwarding with generics**: Provide the correct generic signature for `forwardRef`.197198## Verification1992001. Create the files in the project.2012. Run `npm run build` or `tsc --noEmit` — zero TypeScript errors.2023. Import and render the component in a page or Storybook. Verify it matches the spec.2034. Open React DevTools — confirm `displayName` is set and props look clean.2045. Run Storybook and interact with all controls.2056. Write a quick test using the `data-testid` and run it with your test runner.2067. Check accessibility with axe or Lighthouse on a story that exercises all states.2078. Success: Component is typed correctly, renders without warnings, passes tests, and is easy for other developers to use and extend.208209## References210211- [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/)212- [Storybook for React](https://storybook.js.org/docs/react/get-started/introduction)213- [React.forwardRef Documentation](https://react.dev/reference/react/forwardRef)214- [React.memo](https://react.dev/reference/react/memo)215- Project's existing component library for style alignment (ask user for link or examples)