Skip to Content
Browse all templates at capricorn.build →
Next.jsCustomizationComponents & UI

Components & UI

Learn how to use and customize the reusable components in your template.

Component Organization

Capricorn templates organize components into logical folders with barrel exports for clean imports.

Folder Structure

components/ ├── forms/ │ ├── BookingForm.tsx │ ├── ContactForm.tsx │ ├── NewsletterForm.tsx │ └── index.tsx ← Barrel export ├── layout/ │ ├── Header.tsx │ ├── Footer.tsx │ └── index.tsx ← Barrel export ├── sections/ │ ├── home/ │ │ ├── Hero.tsx │ │ ├── Services.tsx │ │ └── index.tsx ← Barrel export │ └── about/ │ └── index.tsx ├── shared/ │ ├── PageHero.tsx │ ├── BlogCard.tsx │ ├── ServiceCard.tsx │ └── index.tsx ← Barrel export └── ui/ ├── Button.tsx ├── Input.tsx ├── Icon.tsx └── index.tsx ← Barrel export

Barrel Exports

Each component folder has an index.tsx file that re-exports all components. This allows clean, organized imports.

How Barrel Exports Work

The index.tsx file:

// components/ui/index.tsx export { Button } from "./Button"; export { Input } from "./Input"; export { Select } from "./Select"; export { Textarea } from "./Textarea"; export { Accordion } from "./Accordion"; export { Icon } from "./Icon";

Clean imports in your code:

// ✅ Clean - import from folder import { Button, Input, Icon } from "@/components/ui"; // ❌ Verbose - import from individual files import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import { Icon } from "@/components/ui/Icon";

Adding New Components to Barrel Exports

When you create a new component, add it to the folder’s index.tsx:

// 1. Create your component // components/ui/Badge.tsx export function Badge({ children, variant = "default" }) { return ( <span className={`badge badge-${variant}`}> {children} </span> ); } // 2. Add to index.tsx // components/ui/index.tsx export { Button } from "./Button"; export { Input } from "./Input"; export { Badge } from "./Badge"; // ← Add this line

Using Components

UI Components

Base UI elements like buttons, inputs, and icons:

import { Button, Input, Select, Textarea, Icon, Accordion } from "@/components/ui"; // Button variants <Button>Primary Button</Button> <Button variant="secondary">Secondary</Button> <Button variant="outline">Outline</Button> <Button variant="ghost">Ghost</Button> // Button sizes <Button size="sm">Small</Button> <Button size="md">Medium</Button> <Button size="lg">Large</Button> // Input with label <Input label="Email" type="email" placeholder="you@example.com" required /> // Select dropdown <Select label="Service" options={[ { value: "cleaning", label: "Teeth Cleaning" }, { value: "whitening", label: "Teeth Whitening" }, ]} placeholder="Select a service" /> // Icon (varies by template) <Icon name="Phone" size={24} /> <Icon name="Calendar" weight="bold" />

Form Components

Pre-built forms for common use cases:

import { BookingForm, ContactForm, NewsletterForm } from "@/components/forms"; // Booking/Appointment form <BookingForm services={serviceOptions} /> // Contact form <ContactForm /> // Newsletter signup <NewsletterForm />

Shared Components

Reusable components used across multiple pages:

import { PageHero, BlogCard, ServiceCard, TeamMemberCard, TestimonialCard, PricingCard, } from "@/components/shared"; // Page header <PageHero title="Our Services" description="Explore our range of dental services" breadcrumbs={[ { label: "Home", href: "/" }, { label: "Services", href: "/services" }, ]} /> // Content cards <BlogCard post={post} /> <ServiceCard service={service} /> <TeamMemberCard member={member} />

Layout Components

Site-wide layout elements:

import { Header, Footer } from "@/components/layout"; // Used in app/layout.tsx <Header /> <main>{children}</main> <Footer />

Section Components

Page-specific sections organized by page:

import { Hero, Services, Testimonials, CTA } from "@/components/sections/home"; // Homepage sections <Hero /> <Services /> <Testimonials /> <CTA />

Customizing Components

Method 1: Props

Most components accept props for customization:

<Button variant="primary" size="lg" fullWidth className="custom-class" > Book Now </Button> <PageHero title="Custom Title" centered size="large" />

Method 2: Edit the Component File

For deeper customization, edit the component directly:

// components/ui/Button.tsx export function Button({ variant = "primary", size = "md", children, ...props }) { const variants = { primary: "bg-primary text-white hover:bg-primary-dark", secondary: "bg-secondary text-white hover:bg-secondary-dark", outline: "border-2 border-primary text-primary hover:bg-primary/10", ghost: "text-foreground hover:bg-foreground/5", // Add your custom variant success: "bg-green-600 text-white hover:bg-green-700", }; const sizes = { sm: "h-10 px-4 text-sm", md: "h-11 px-6 text-sm", lg: "h-13 px-8 text-sm", }; return ( <button className={cn(baseStyles, variants[variant], sizes[size])} {...props} > {children} </button> ); }

Method 3: Extend with className

Add custom styles via the className prop:

<Button className="rounded-none shadow-lg"> Square Button with Shadow </Button> <Input className="border-2 border-blue-500" />

Creating New Components

Simple Component

// components/shared/Alert.tsx import { cn } from "@/lib/utils"; import { Icon } from "@/components/ui"; interface AlertProps { type?: "info" | "success" | "warning" | "error"; title: string; message: string; } export function Alert({ type = "info", title, message }: AlertProps) { const styles = { info: "bg-blue-50 text-blue-900 border-blue-200", success: "bg-green-50 text-green-900 border-green-200", warning: "bg-yellow-50 text-yellow-900 border-yellow-200", error: "bg-red-50 text-red-900 border-red-200", }; const icons = { info: "Info", success: "CheckCircle", warning: "Warning", error: "XCircle", }; return ( <div className={cn("p-4 rounded-lg border", styles[type])}> <div className="flex gap-3"> <Icon name={icons[type]} size={20} /> <div> <h4 className="font-semibold">{title}</h4> <p className="text-sm opacity-80">{message}</p> </div> </div> </div> ); }

Then add to the barrel export:

// components/shared/index.tsx export { Alert } from "./Alert";

Component with Children

// components/shared/Section.tsx interface SectionProps { title: string; subtitle?: string; children: React.ReactNode; className?: string; } export function Section({ title, subtitle, children, className }: SectionProps) { return ( <section className={cn("py-16 lg:py-24", className)}> <div className="container mx-auto"> <div className="text-center mb-12"> <h2 className="text-3xl lg:text-4xl font-heading">{title}</h2> {subtitle && ( <p className="mt-4 text-foreground/70">{subtitle}</p> )} </div> {children} </div> </section> ); } // Usage <Section title="Our Services" subtitle="What we offer"> <ServiceGrid services={services} /> </Section>

The cn() Utility

All templates include a cn() utility for merging class names:

import { cn } from "@/lib/utils"; // Combines classes intelligently cn("px-4 py-2", "bg-blue-500") // → "px-4 py-2 bg-blue-500" // Handles conditionals cn("base-class", isActive && "active-class") // Resolves Tailwind conflicts cn("px-4", "px-8") // → "px-8" (later class wins)

Component Patterns

Conditional Rendering

export function Feature({ isActive, title, description }) { if (!isActive) return null; return ( <div> <h3>{title}</h3> <p>{description}</p> </div> ); }

List Rendering

export function ServiceList({ services }) { return ( <div className="grid grid-cols-1 md:grid-cols-3 gap-6"> {services.map((service) => ( <ServiceCard key={service.id} service={service} /> ))} </div> ); }

Composition

Build complex UIs from simple components:

<Card> <Card.Header> <Card.Title>Service Name</Card.Title> </Card.Header> <Card.Body> <p>Service description...</p> </Card.Body> <Card.Footer> <Button>Learn More</Button> </Card.Footer> </Card>

Best Practices

  1. Use barrel exports — Keep imports clean and organized
  2. Keep components focused — One component, one purpose
  3. Use TypeScript interfaces — Define prop types for safety
  4. Follow naming conventions — PascalCase for components
  5. Co-locate related files — Keep component, styles, and tests together
  6. Use the cn() utility — For merging class names safely
  7. Accept className prop — Allow style overrides
  8. Document complex components — Add comments for non-obvious logic

Template-Specific Components

Each template may have unique components. Check your template’s documentation:


Next Steps:

Last updated on