Claude Code Plugins

Community-maintained marketplace

Feedback

Build production-ready React components with TypeScript, tests, and documentation using modern best practices

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name react-component-builder
description Build production-ready React components with TypeScript, tests, and documentation using modern best practices
allowed-tools Read, Write, Edit, Grep, Glob, Bash

React Component Builder

Build complete React components with TypeScript, tests, Storybook stories, and documentation.

When to Use

  • Creating new React components
  • Building component libraries
  • Developing reusable UI elements
  • Implementing design systems

Component Structure

components/
  ComponentName/
    ├── index.ts                 # Barrel export
    ├── ComponentName.tsx        # Main component
    ├── ComponentName.test.tsx   # Unit tests
    ├── ComponentName.stories.tsx # Storybook stories
    ├── ComponentName.styles.ts  # Styled-components or CSS modules
    ├── types.ts                 # TypeScript interfaces
    └── README.md                # Component documentation

Workflow

1. Define Component Requirements

// Gather requirements:
// - What props does it need?
// - What state will it manage?
// - What events will it emit?
// - What styles are needed?
// - What variants exist?

2. Create TypeScript Interfaces

// types.ts
export interface ComponentNameProps {
  /** Primary content to display */
  children: React.ReactNode;

  /** Component variant */
  variant?: 'primary' | 'secondary' | 'outline';

  /** Size of the component */
  size?: 'sm' | 'md' | 'lg';

  /** Whether component is disabled */
  disabled?: boolean;

  /** Click handler */
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;

  /** Additional CSS class */
  className?: string;

  /** Test ID for testing */
  testId?: string;
}

3. Build Component

// ComponentName.tsx
import React from 'react';
import { ComponentNameProps } from './types';
import * as S from './ComponentName.styles';

export const ComponentName: React.FC<ComponentNameProps> = ({
  children,
  variant = 'primary',
  size = 'md',
  disabled = false,
  onClick,
  className,
  testId = 'component-name',
}) => {
  // State management
  const [isActive, setIsActive] = React.useState(false);

  // Event handlers
  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    if (disabled) return;
    setIsActive(!isActive);
    onClick?.(event);
  };

  // Render
  return (
    <S.Container
      variant={variant}
      size={size}
      disabled={disabled}
      isActive={isActive}
      onClick={handleClick}
      className={className}
      data-testid={testId}
    >
      {children}
    </S.Container>
  );
};

ComponentName.displayName = 'ComponentName';

4. Add Styling

Option A: Styled-Components

// ComponentName.styles.ts
import styled, { css } from 'styled-components';

interface ContainerProps {
  variant: 'primary' | 'secondary' | 'outline';
  size: 'sm' | 'md' | 'lg';
  disabled: boolean;
  isActive: boolean;
}

const sizeStyles = {
  sm: css`
    padding: 0.5rem 1rem;
    font-size: 0.875rem;
  `,
  md: css`
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
  `,
  lg: css`
    padding: 1rem 2rem;
    font-size: 1.125rem;
  `,
};

const variantStyles = {
  primary: css`
    background: #0066cc;
    color: white;
    border: 2px solid #0066cc;

    &:hover:not(:disabled) {
      background: #0052a3;
    }
  `,
  secondary: css`
    background: #6c757d;
    color: white;
    border: 2px solid #6c757d;

    &:hover:not(:disabled) {
      background: #5a6268;
    }
  `,
  outline: css`
    background: transparent;
    color: #0066cc;
    border: 2px solid #0066cc;

    &:hover:not(:disabled) {
      background: #0066cc;
      color: white;
    }
  `,
};

export const Container = styled.button<ContainerProps>`
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.375rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;

  ${({ size }) => sizeStyles[size]}
  ${({ variant }) => variantStyles[variant]}

  ${({ disabled }) =>
    disabled &&
    css`
      opacity: 0.5;
      cursor: not-allowed;
    `}

  ${({ isActive }) =>
    isActive &&
    css`
      box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.3);
    `}
`;

Option B: Tailwind CSS

// ComponentName.tsx
import clsx from 'clsx';

const sizeClasses = {
  sm: 'px-4 py-2 text-sm',
  md: 'px-6 py-3 text-base',
  lg: 'px-8 py-4 text-lg',
};

const variantClasses = {
  primary: 'bg-blue-600 text-white border-blue-600 hover:bg-blue-700',
  secondary: 'bg-gray-600 text-white border-gray-600 hover:bg-gray-700',
  outline: 'bg-transparent text-blue-600 border-blue-600 hover:bg-blue-600 hover:text-white',
};

// In component:
<button
  className={clsx(
    'inline-flex items-center justify-center rounded-md font-semibold border-2 transition-all',
    sizeClasses[size],
    variantClasses[variant],
    disabled && 'opacity-50 cursor-not-allowed',
    isActive && 'ring-4 ring-blue-300',
    className
  )}
>
  {children}
</button>

5. Write Tests

// ComponentName.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { ComponentName } from './ComponentName';

describe('ComponentName', () => {
  it('renders children correctly', () => {
    render(<ComponentName>Click me</ComponentName>);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });

  it('applies correct variant styles', () => {
    render(<ComponentName variant="primary">Button</ComponentName>);
    const button = screen.getByTestId('component-name');
    // Add assertions for styles
  });

  it('handles click events', () => {
    const handleClick = jest.fn();
    render(<ComponentName onClick={handleClick}>Click</ComponentName>);

    fireEvent.click(screen.getByText('Click'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('does not trigger onClick when disabled', () => {
    const handleClick = jest.fn();
    render(
      <ComponentName disabled onClick={handleClick}>
        Disabled
      </ComponentName>
    );

    fireEvent.click(screen.getByText('Disabled'));
    expect(handleClick).not.toHaveBeenCalled();
  });

  it('renders all size variants', () => {
    const { rerender } = render(<ComponentName size="sm">Small</ComponentName>);
    // Assert sm size

    rerender(<ComponentName size="md">Medium</ComponentName>);
    // Assert md size

    rerender(<ComponentName size="lg">Large</ComponentName>);
    // Assert lg size
  });
});

6. Create Storybook Stories

// ComponentName.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentName } from './ComponentName';

const meta: Meta<typeof ComponentName> = {
  title: 'Components/ComponentName',
  component: ComponentName,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'outline'],
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
    },
    disabled: {
      control: 'boolean',
    },
  },
};

export default meta;
type Story = StoryObj<typeof ComponentName>;

export const Primary: Story = {
  args: {
    children: 'Primary Button',
    variant: 'primary',
  },
};

export const Secondary: Story = {
  args: {
    children: 'Secondary Button',
    variant: 'secondary',
  },
};

export const Outline: Story = {
  args: {
    children: 'Outline Button',
    variant: 'outline',
  },
};

export const Small: Story = {
  args: {
    children: 'Small Button',
    size: 'sm',
  },
};

export const Large: Story = {
  args: {
    children: 'Large Button',
    size: 'lg',
  },
};

export const Disabled: Story = {
  args: {
    children: 'Disabled Button',
    disabled: true,
  },
};

export const AllVariants: Story = {
  render: () => (
    <div style={{ display: 'flex', gap: '1rem', flexDirection: 'column' }}>
      <div style={{ display: 'flex', gap: '1rem' }}>
        <ComponentName variant="primary">Primary</ComponentName>
        <ComponentName variant="secondary">Secondary</ComponentName>
        <ComponentName variant="outline">Outline</ComponentName>
      </div>
      <div style={{ display: 'flex', gap: '1rem' }}>
        <ComponentName size="sm">Small</ComponentName>
        <ComponentName size="md">Medium</ComponentName>
        <ComponentName size="lg">Large</ComponentName>
      </div>
    </div>
  ),
};

7. Add Documentation

# ComponentName

Brief description of what this component does.

## Usage

\`\`\`tsx
import { ComponentName } from './components/ComponentName';

function App() {
  return (
    <ComponentName variant="primary" onClick={() => console.log('Clicked!')}>
      Click Me
    </ComponentName>
  );
}
\`\`\`

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | - | Content to display |
| variant | 'primary' \| 'secondary' \| 'outline' | 'primary' | Visual style variant |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Component size |
| disabled | boolean | false | Whether component is disabled |
| onClick | function | - | Click event handler |
| className | string | - | Additional CSS class |
| testId | string | 'component-name' | Test ID for testing |

## Examples

### Basic Usage
\`\`\`tsx
<ComponentName>Click me</ComponentName>
\`\`\`

### With Variant
\`\`\`tsx
<ComponentName variant="secondary">Secondary Action</ComponentName>
\`\`\`

### With Click Handler
\`\`\`tsx
<ComponentName onClick={() => alert('Clicked!')}>
  Interactive Button
</ComponentName>
\`\`\`

## Accessibility

- Uses semantic HTML button element
- Supports keyboard navigation
- Proper focus states
- ARIA attributes for screen readers

## Testing

Run tests:
\`\`\`bash
npm test ComponentName.test.tsx
\`\`\`

View in Storybook:
\`\`\`bash
npm run storybook
\`\`\`

8. Create Barrel Export

// index.ts
export { ComponentName } from './ComponentName';
export type { ComponentNameProps } from './types';

Best Practices

DO:

  • Use TypeScript for type safety
  • Write comprehensive tests (aim for >80% coverage)
  • Document all props
  • Create Storybook stories
  • Follow naming conventions
  • Use semantic HTML
  • Support accessibility
  • Handle edge cases

DON'T:

  • Skip prop validation
  • Forget error boundaries
  • Ignore accessibility
  • Leave console.logs in code
  • Skip tests for edge cases
  • Use inline styles without reason

Quick Templates

Functional Component

export const ComponentName: React.FC<Props> = (props) => {
  return <div>{props.children}</div>;
};

With Hooks

export const ComponentName: React.FC<Props> = (props) => {
  const [state, setState] = useState();

  useEffect(() => {
    // Effect logic
  }, []);

  return <div></div>;
};

Forwarding Refs

export const ComponentName = forwardRef<HTMLDivElement, Props>(
  (props, ref) => {
    return <div ref={ref}>{props.children}</div>;
  }
);

Related Tools

  • ESLint: Linting
  • Prettier: Code formatting
  • Jest: Testing framework
  • React Testing Library: Component testing
  • Storybook: Component development
  • TypeScript: Type checking