name: scaffold description: Scaffold components, pages, features, and API routes with types, tests, and stories. Use when asked to create a new component, page, feature module, or API route author: subinium user-invocable: true args: type and name (e.g., "component Button", "page dashboard", "feature auth")
Scaffold
Generate project scaffolding with consistent structure. Uses $ARGUMENTS for type and name.
Usage
/scaffold component Button
/scaffold page dashboard/settings
/scaffold feature auth
/scaffold api users
Component: /scaffold component [Name]
Create at components/[name]/:
components/Button/
├── Button.tsx # Component implementation
├── Button.test.tsx # Unit tests (vitest/jest)
├── Button.stories.tsx # Storybook story (if project uses storybook)
└── index.ts # Barrel export
Template: Button.tsx
import { cn } from '@/lib/utils';
interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
className?: string;
onClick?: () => void;
}
export const Button = ({
children,
variant = 'primary',
size = 'md',
disabled = false,
className,
onClick,
}: ButtonProps) => {
return (
<button
className={cn(
'rounded-lg font-medium transition-colors',
/* size */
size === 'sm' && 'px-3 py-1.5 text-sm',
size === 'md' && 'px-4 py-2 text-base',
size === 'lg' && 'px-6 py-3 text-lg',
/* variant */
variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
variant === 'secondary' && 'bg-gray-100 text-gray-900 hover:bg-gray-200',
variant === 'ghost' && 'bg-transparent hover:bg-gray-100',
disabled && 'cursor-not-allowed opacity-50',
className
)}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};
Template: Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click</Button>);
fireEvent.click(screen.getByText('Click'));
expect(onClick).toHaveBeenCalledOnce();
});
it('does not call onClick when disabled', () => {
const onClick = vi.fn();
render(<Button onClick={onClick} disabled>Click</Button>);
fireEvent.click(screen.getByText('Click'));
expect(onClick).not.toHaveBeenCalled();
});
});
Page: /scaffold page [path]
Create Next.js App Router page at app/[path]/:
app/dashboard/settings/
├── page.tsx # Page component (Server Component)
├── loading.tsx # Loading UI (skeleton)
├── error.tsx # Error boundary ('use client')
└── layout.tsx # Layout (only if page needs its own layout)
page.tsx: Server Component by default, async for data fetchingloading.tsx: Skeleton matching the page layouterror.tsx:'use client'with retry button and error message- Generate
generateMetadata()for SEO
Feature: /scaffold feature [name]
Create a full feature module:
features/auth/
├── components/ # Feature-specific components
│ └── LoginForm.tsx
├── hooks/ # Feature-specific hooks
│ └── useAuth.ts
├── lib/ # Feature logic, API calls
│ └── api.ts
├── types/ # Feature types
│ └── index.ts
└── index.ts # Public API barrel export
- Only export what other features need from
index.ts - Keep internal implementation private
- Include types for all public interfaces
API Route: /scaffold api [name]
Create at app/api/[name]/:
app/api/users/
└── route.ts # Route handler (GET, POST, PUT, DELETE)
- Type-safe request/response with Zod validation
- Error handling with proper HTTP status codes
- Include all CRUD handlers as stubs
Pre-Flight (Before Generating ANY Files)
- Read
package.jsonto detect: test framework (vitest/jest), styling (tailwind/css-modules), component library (shadcn/radix) - Read
tsconfig.jsonto detect path aliases (@/) - Check existing
components/orsrc/components/to match naming conventions - Check if project uses barrel exports (
index.ts) - Check if project uses Storybook (
storybookin devDependencies)
Rules
- Match existing project structure and conventions — never guess
- Use the project's existing test framework (vitest/jest/playwright)
- Use the project's existing styling approach (Tailwind/CSS Modules/styled-components)
- Add barrel export (
index.ts) only if the project already uses them - After generating, run
npx tsc --noEmitand lint to verify