Files
skript-ui/.agent/commands/nextjs-component-generator.md
2026-01-28 02:31:48 +03:00

11 KiB

allowed-tools: Read, Write, Edit argument-hint: [component-name] [--client] [--server] [--page] [--layout] description: Generate optimized React components for Next.js with TypeScript and best practices

Next.js Component Generator

Component Name: $ARGUMENTS

Project Context Analysis

Framework Detection

  • Next.js config: @next.config.js
  • TypeScript config: @tsconfig.json (if exists)
  • Tailwind config: @tailwind.config.js (if exists)
  • Package.json: @package.json

Existing Component Patterns

  • Components directory: @components/
  • App directory: @app/ (if App Router)
  • Pages directory: @pages/ (if Pages Router)
  • Styles directory: @styles/

Component Generation Requirements

1. Component Type Detection

Based on arguments and context, determine component type:

  • Client Component: Interactive UI with state/events (--client or default for interactive components)
  • Server Component: Static rendering, data fetching (--server or default for Next.js 13+)
  • Page Component: Route-level component (--page)
  • Layout Component: Shared layout wrapper (--layout)

2. File Structure Creation

Generate comprehensive component structure:

components/[ComponentName]/
├── index.ts                    # Barrel export
├── [ComponentName].tsx         # Main component
├── [ComponentName].module.css  # Component styles
├── [ComponentName].test.tsx    # Unit tests
├── [ComponentName].stories.tsx # Storybook story (if detected)
└── types.ts                   # TypeScript types

3. Component Templates

Server Component Template

import { FC } from 'react';
import styles from './ComponentName.module.css';

interface ComponentNameProps {
  /**
   * Component description
   */
  children?: React.ReactNode;
  /**
   * Additional CSS classes
   */
  className?: string;
}

/**
 * ComponentName - Server Component
 * 
 * @description Brief description of component purpose
 * @example
 * <ComponentName>Content</ComponentName>
 */
export const ComponentName: FC<ComponentNameProps> = ({
  children,
  className = '',
  ...props
}) => {
  return (
    <div className={`${styles.container} ${className}`} {...props}>
      {children}
    </div>
  );
};

export default ComponentName;

Client Component Template

'use client';

import { FC, useState, useEffect } from 'react';
import styles from './ComponentName.module.css';

interface ComponentNameProps {
  /**
   * Component description
   */
  children?: React.ReactNode;
  /**
   * Click event handler
   */
  onClick?: () => void;
  /**
   * Additional CSS classes
   */
  className?: string;
}

/**
 * ComponentName - Client Component
 * 
 * @description Interactive component with client-side functionality
 * @example
 * <ComponentName onClick={() => console.log('clicked')}>
 *   Content
 * </ComponentName>
 */
export const ComponentName: FC<ComponentNameProps> = ({
  children,
  onClick,
  className = '',
  ...props
}) => {
  const [isActive, setIsActive] = useState(false);

  const handleClick = () => {
    setIsActive(!isActive);
    onClick?.();
  };

  return (
    <button
      className={`${styles.button} ${isActive ? styles.active : ''} ${className}`}
      onClick={handleClick}
      {...props}
    >
      {children}
    </button>
  );
};

export default ComponentName;

Page Component Template

import { Metadata } from 'next';
import ComponentName from '@/components/ComponentName';

export const metadata: Metadata = {
  title: 'Page Title',
  description: 'Page description',
};

interface PageProps {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
}

export default function Page({ params, searchParams }: PageProps) {
  return (
    <main>
      <h1>Page Title</h1>
      <ComponentName />
    </main>
  );
}

Layout Component Template

import { FC } from 'react';
import styles from './Layout.module.css';

interface LayoutProps {
  children: React.ReactNode;
  /**
   * Page title
   */
  title?: string;
}

/**
 * Layout - Shared layout component
 * 
 * @description Provides consistent layout structure across pages
 */
export const Layout: FC<LayoutProps> = ({
  children,
  title,
}) => {
  return (
    <div className={styles.layout}>
      <header className={styles.header}>
        {title && <h1 className={styles.title}>{title}</h1>}
      </header>
      
      <main className={styles.main}>
        {children}
      </main>
      
      <footer className={styles.footer}>
        <p>&copy; 2024 Your App</p>
      </footer>
    </div>
  );
};

export default Layout;

4. CSS Module Templates

Basic Component Styles

/* ComponentName.module.css */
.container {
  display: flex;
  flex-direction: column;
  padding: 1rem;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
  background-color: #ffffff;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 1rem;
  border-radius: 6px;
  border: 1px solid transparent;
  background-color: #3b82f6;
  color: white;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
}

.button:hover {
  background-color: #2563eb;
}

.button:focus {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

.button.active {
  background-color: #1d4ed8;
}

/* Responsive design */
@media (max-width: 768px) {
  .container {
    padding: 0.75rem;
  }
  
  .button {
    padding: 0.75rem 1rem;
  }
}

Layout Styles

/* Layout.module.css */
.layout {
  min-height: 100vh;
  display: grid;
  grid-template-rows: auto 1fr auto;
}

.header {
  padding: 1rem 2rem;
  background-color: #f8fafc;
  border-bottom: 1px solid #e2e8f0;
}

.title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
  color: #1e293b;
}

.main {
  padding: 2rem;
  max-width: 1200px;
  margin: 0 auto;
  width: 100%;
}

.footer {
  padding: 1rem 2rem;
  background-color: #f1f5f9;
  border-top: 1px solid #e2e8f0;
  text-align: center;
  color: #64748b;
}

5. TypeScript Types

// types.ts
export interface BaseComponentProps {
  children?: React.ReactNode;
  className?: string;
  'data-testid'?: string;
}

export interface ButtonProps extends BaseComponentProps {
  variant?: 'primary' | 'secondary' | 'outline';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
  onClick?: () => void;
}

export interface LayoutProps extends BaseComponentProps {
  title?: string;
  sidebar?: React.ReactNode;
  breadcrumbs?: BreadcrumbItem[];
}

export interface BreadcrumbItem {
  label: string;
  href?: string;
  current?: boolean;
}

6. Unit Tests

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

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

  it('applies custom className', () => {
    render(<ComponentName className="custom-class">Test</ComponentName>);
    const element = screen.getByText('Test');
    expect(element).toHaveClass('custom-class');
  });

  it('handles click events', () => {
    const handleClick = jest.fn();
    render(<ComponentName onClick={handleClick}>Click me</ComponentName>);
    
    const button = screen.getByText('Click me');
    fireEvent.click(button);
    
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('toggles active state on click', () => {
    render(<ComponentName>Toggle</ComponentName>);
    const button = screen.getByText('Toggle');
    
    expect(button).not.toHaveClass('active');
    
    fireEvent.click(button);
    expect(button).toHaveClass('active');
    
    fireEvent.click(button);
    expect(button).not.toHaveClass('active');
  });

  it('is accessible', () => {
    render(<ComponentName>Accessible Button</ComponentName>);
    const button = screen.getByRole('button');
    
    expect(button).toBeInTheDocument();
    expect(button).toHaveAccessibleName('Accessible Button');
  });
});

7. Storybook Stories (if detected)

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

const meta: Meta<typeof ComponentName> = {
  title: 'Components/ComponentName',
  component: ComponentName,
  parameters: {
    layout: 'centered',
    docs: {
      description: {
        component: 'A reusable component built for Next.js applications.',
      },
    },
  },
  tags: ['autodocs'],
  argTypes: {
    onClick: { action: 'clicked' },
    className: { control: 'text' },
  },
};

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

export const Default: Story = {
  args: {
    children: 'Default Component',
  },
};

export const WithCustomClass: Story = {
  args: {
    children: 'Custom Styled',
    className: 'custom-style',
  },
};

export const Interactive: Story = {
  args: {
    children: 'Click me',
    onClick: () => alert('Component clicked!'),
  },
};

8. Barrel Export

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

Framework-Specific Optimizations

Tailwind CSS Integration (if detected)

Replace CSS modules with Tailwind classes:

export const ComponentName: FC<ComponentNameProps> = ({
  children,
  className = '',
}) => {
  return (
    <div className={`flex flex-col p-4 rounded-lg border border-slate-200 bg-white ${className}`}>
      {children}
    </div>
  );
};

Next.js App Router Optimizations

  • Server Components: Default for non-interactive components
  • Client Components: Explicit 'use client' directive
  • Metadata: Include metadata for page components
  • Loading States: Implement loading.tsx for async components

Accessibility Features

  • ARIA Labels: Proper labeling for screen readers
  • Keyboard Navigation: Tab order and keyboard shortcuts
  • Focus Management: Visible focus indicators
  • Semantic HTML: Proper semantic elements

Component Generation Process

  1. Analysis: Analyze existing project structure and patterns
  2. Template Selection: Choose appropriate template based on component type
  3. Customization: Adapt template to project conventions
  4. File Creation: Generate all component files
  5. Integration: Update index files and exports
  6. Validation: Verify component compiles and tests pass

Quality Checklist

  • Component follows project naming conventions
  • TypeScript types are properly defined
  • CSS follows established patterns (modules or Tailwind)
  • Unit tests cover key functionality
  • Component is accessible (ARIA, keyboard navigation)
  • Documentation includes usage examples
  • Storybook story created (if Storybook detected)
  • Component compiles without errors
  • Tests pass successfully

Provide the complete component implementation with all specified files and features.