This skill should be used when the user asks to 'create a component', 'add a button', 'build a card', 'add UI element', or 'create a feature component'...
components/ui/*.tsx - shadcn/ui base components (Radix primitives)components/features/*.tsx - Feature-specific componentscomponents/shared/*.tsx - Reusable shared componentscomponents/layout/*.tsx - Layout componentscomponents/mdx/*.tsx - MDX-specific componentscomponents/providers/*.tsx - Context providersnpx shadcn@latest add <component>components/ui/ following CVA patterncomponents/features/contact-form.tsx, articles-list.tsx@/components/ui/components/shared/"use client" directive at top of fileimport { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/utils";
const componentVariants = cva(
"base-classes-here",
{
variants: {
variant: {
default: "variant-default-classes",
secondary: "variant-secondary-classes",
},
size: {
default: "size-default-classes",
sm: "size-sm-classes",
lg: "size-lg-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ComponentProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof componentVariants> {}
const Component = React.forwardRef<HTMLDivElement, ComponentProps>(
({ className, variant, size, ...props }, ref) => {
return (
<div
ref={ref}
className={cn(componentVariants({ variant, size, className }))}
{...props}
/>
);
}
);
Component.displayName = "Component";
export { Component, componentVariants };
import { ComponentName } from "@/components/ui/component-name";
interface FeatureProps {
title: string;
items: Array<{ id: string; name: string }>;
}
export function FeatureName({ title, items }: FeatureProps) {
return (
<section>
<h2>{title}</h2>
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</section>
);
}
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
interface InteractiveProps {
initialValue: string;
}
export function InteractiveComponent({ initialValue }: InteractiveProps) {
const [value, setValue] = useState(initialValue);
return (
<div>
<span>{value}</span>
<Button onClick={() => setValue("clicked")}>Click me</Button>
</div>
);
}
import { cn } from "@/lib/utils";
interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
export function Container({ className, children, ...props }: ContainerProps) {
return (
<div className={cn("mx-auto max-w-7xl px-4", className)} {...props}>
{children}
</div>
);
}
"use client" when not needed (adds to bundle)cn() utility for className mergingdisplayName on forwardRef componentsAfter changes, run:
.claude/skills/component-layer/scripts/validate-component-patterns.sh <file>
pnpm typecheck # Verify TypeScript
pnpm check # Biome lint/format
components/
āāā ui/ # shadcn/ui primitives (button, card, etc.)
āāā features/ # Feature-specific (contact-form, articles-list)
āāā shared/ # Reusable across features (avatar, social-icons)
āāā layout/ # Layout components (container, header, footer)
āāā mdx/ # MDX rendering components (code, code-tabs)
āāā providers/ # Context providers (theme, posthog)