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 exportBarrel 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 lineUsing 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
- Use barrel exports — Keep imports clean and organized
- Keep components focused — One component, one purpose
- Use TypeScript interfaces — Define prop types for safety
- Follow naming conventions — PascalCase for components
- Co-locate related files — Keep component, styles, and tests together
- Use the
cn()utility — For merging class names safely - Accept
classNameprop — Allow style overrides - 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: