--- name: component-architecture description: Reusable component patterns for cards, sections, forms, and layouts with consistent prop interfaces and composition strategies. Use when creating new components, refactoring existing ones, or establishing component design patterns. --- # Component Architecture ## Folder Structure ``` components/ ├── ui/ # shadcn/ui base components │ ├── button.tsx │ ├── card.tsx │ └── ... ├── layout/ # Page structure components │ ├── Header.tsx │ ├── Footer.tsx │ ├── MobileNav.tsx │ └── LanguageSwitcher.tsx ├── sections/ # Home page sections │ ├── Hero.tsx │ ├── TrustRow.tsx │ ├── ServicesPreview.tsx │ ├── AboutPreview.tsx │ └── LeadCapture.tsx ├── cards/ # Reusable card components │ ├── ServiceCard.tsx │ ├── ProgrammeCard.tsx │ ├── TestimonialCard.tsx │ └── PricingCard.tsx ├── forms/ # Form components │ ├── ContactForm.tsx │ ├── BookingForm.tsx │ └── LeadCaptureForm.tsx ├── shared/ # Utility components │ ├── Container.tsx │ ├── SectionHeader.tsx │ ├── CTAButton.tsx │ └── Breadcrumbs.tsx └── seo/ # SEO components ├── JsonLd.tsx └── OrganizationJsonLd.tsx ``` ## Component Template ```tsx // components/cards/ServiceCard.tsx import Link from 'next/link'; import { ArrowRight } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import type { Service } from '@/types'; interface ServiceCardProps { service: Service; locale: string; } export function ServiceCard({ service, locale }: ServiceCardProps) { return (
{service.name} {service.duration}

{service.shortDesc}

{service.tags.map((tag) => ( {tag} ))}
From €{service.priceFrom} Learn more
); } ``` ## Section Component Pattern ```tsx // components/sections/ServicesPreview.tsx import { getTranslations } from 'next-intl/server'; import { getServices } from '@/lib/data'; import { Container } from '@/components/shared/Container'; import { SectionHeader } from '@/components/shared/SectionHeader'; import { ServiceCard } from '@/components/cards/ServiceCard'; import { CTAButton } from '@/components/shared/CTAButton'; import type { Locale } from '@/i18n.config'; interface ServicesPreviewProps { locale: Locale; } export async function ServicesPreview({ locale }: ServicesPreviewProps) { const t = await getTranslations({ locale, namespace: 'home.services' }); const services = await getServices(locale); const featured = services.slice(0, 4); return (
{featured.map((service) => ( ))}
{t('viewAll')}
); } ``` ## Shared Components ```tsx // components/shared/Container.tsx import { cn } from '@/lib/utils'; interface ContainerProps { children: React.ReactNode; className?: string; size?: 'default' | 'narrow' | 'wide'; } export function Container({ children, className, size = 'default', }: ContainerProps) { return (
{children}
); } ``` ```tsx // components/shared/SectionHeader.tsx import { cn } from '@/lib/utils'; interface SectionHeaderProps { title: string; subtitle?: string; centered?: boolean; className?: string; } export function SectionHeader({ title, subtitle, centered = false, className, }: SectionHeaderProps) { return (

{title}

{subtitle && (

{subtitle}

)}
); } ``` ```tsx // components/shared/CTAButton.tsx import Link from 'next/link'; import { Button, type ButtonProps } from '@/components/ui/button'; import { ArrowRight } from 'lucide-react'; interface CTAButtonProps extends ButtonProps { href: string; children: React.ReactNode; showArrow?: boolean; } export function CTAButton({ href, children, showArrow = false, ...props }: CTAButtonProps) { return ( ); } ``` ## Composition Pattern ```tsx // Compound components for flexibility // components/cards/PricingCard.tsx import { Card, CardContent, CardHeader } from '@/components/ui/card'; interface PricingCardProps { children: React.ReactNode; featured?: boolean; } export function PricingCard({ children, featured }: PricingCardProps) { return ( {children} ); } PricingCard.Header = function PricingCardHeader({ title, price, period, }: { title: string; price: number; period?: string; }) { return (

{title}

€{price} {period && /{period}}
); }; PricingCard.Features = function PricingCardFeatures({ features, }: { features: string[]; }) { return ( ); }; // Usage ``` ## Props Interface Conventions ```tsx // Consistent prop naming interface ComponentProps { // Content title: string; subtitle?: string; description?: string; // Data items: Item[]; data?: DataType; // Variants variant?: 'default' | 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; // State isLoading?: boolean; disabled?: boolean; // Styling className?: string; // Events onClick?: () => void; onSubmit?: (data: FormData) => void; // i18n locale: string; // Children children?: React.ReactNode; } ``` ## Empty States ```tsx // components/shared/EmptyState.tsx interface EmptyStateProps { title: string; description?: string; action?: React.ReactNode; } export function EmptyState({ title, description, action }: EmptyStateProps) { return (

{title}

{description && (

{description}

)} {action &&
{action}
}
); } ```