Components
Last updated on 2026-08-27
The Email Marketing Kit follows a strict 3-tier component hierarchy. Primitives are never modified, composites combine primitives with domain logic, and pages wire composites together with seed data.
Component Architecture
Tier 3: Pages (app/(app)/**/page.tsx)
└── Import composites + seed data
├── Tier 2: Composites (components/{domain}/)
│ └── Built from primitives + domain logic
│ └── Tier 1: Primitives (components/ui/)
│ └── shadcn/ui — never modified
└── Tier 2: Layout (components/layout/)
└── App shell: sidebar, header, skip-link
Tier 1: Primitives (components/ui/)
These are shadcn/ui components installed via the shadcn CLI. They wrap Radix UI / Base UI headless primitives with Tailwind CSS styling. Do not modify these files directly -- use composites to extend behavior.
| Component | File | Description |
|---|---|---|
| Alert Dialog | alert-dialog.tsx |
Confirmation dialogs with cancel/confirm actions |
| Avatar | avatar.tsx |
User avatar with image fallback to initials |
| Badge | badge.tsx |
Status and label badges with variant colors |
| Breadcrumb | breadcrumb.tsx |
Navigation breadcrumb trail |
| Button | button.tsx |
Primary action buttons with variant and size props |
| Card | card.tsx |
Container card with header, content, and footer slots |
| Chart | chart.tsx |
Recharts wrapper with theme-aware colors |
| Checkbox | checkbox.tsx |
Checkbox input with label |
| Collapsible | collapsible.tsx |
Expandable/collapsible content sections |
| Command | command.tsx |
Command palette with search and keyboard navigation |
| Dialog | dialog.tsx |
Modal dialog overlays |
| Dropdown Menu | dropdown-menu.tsx |
Context menus and action dropdowns |
| Input | input.tsx |
Text input field |
| Input Group | input-group.tsx |
Input with prefix/suffix addons |
| Label | label.tsx |
Form field labels |
| Popover | popover.tsx |
Floating content panels |
| Progress | progress.tsx |
Linear progress bar |
| Radio Group | radio-group.tsx |
Radio button groups |
| Resizable | resizable.tsx |
Resizable panel layouts |
| Scroll Area | scroll-area.tsx |
Scrollable container with custom scrollbars |
| Select | select.tsx |
Dropdown select with search |
| Separator | separator.tsx |
Horizontal or vertical divider |
| Sheet | sheet.tsx |
Slide-out drawer panel (used for block settings, node config) |
| Sidebar | sidebar.tsx |
Collapsible sidebar navigation framework |
| Skeleton | skeleton.tsx |
Loading placeholder shapes |
| Slider | slider.tsx |
Range slider input |
| Sonner | sonner.tsx |
Toast notification container |
| Switch | switch.tsx |
Toggle switch |
| Table | table.tsx |
Data table with header, body, and row components |
| Tabs | tabs.tsx |
Tabbed content navigation |
| Textarea | textarea.tsx |
Multi-line text input |
| Toggle | toggle.tsx |
Toggle button |
| Toggle Group | toggle-group.tsx |
Mutually exclusive toggle group |
| Tooltip | tooltip.tsx |
Hover tooltip |
Total: 34 primitives
Tier 2: Composites
Composites are domain-specific components built from Tier 1 primitives. They contain business logic, data formatting, and layout patterns specific to the email marketing domain.
Dashboard Composites (components/dashboard/)
| Component | File | Description | Key Primitives Used |
|---|---|---|---|
| StatCard | stat-card.tsx |
KPI metric card with value, label, trend, and icon | Card |
| EngagementChart | engagement-chart.tsx |
Subscriber growth line/area chart | Card, Chart |
| EngagementDistribution | engagement-distribution.tsx |
Hot/warm/cold engagement breakdown | Card, Chart |
| RecentCampaigns | recent-campaigns.tsx |
Table of recently sent campaigns with key stats | Card, Table, Badge |
| TopAutomations | top-automations.tsx |
List of top-performing automations by revenue | Card, Badge |
Shared Composites (components/shared/)
| Component | File | Description | Key Primitives Used |
|---|---|---|---|
| PageHeader | page-header.tsx |
Consistent page header with title, description, and actions | -- |
| DataTable | data-table.tsx |
Reusable sortable/filterable data table | Table, Input, Select, Badge |
| StatusBadge | status-badge.tsx |
Color-coded status indicator | Badge |
| EmptyState | empty-state.tsx |
Empty state placeholder with icon, message, and CTA | Button |
Layout Composites (components/layout/)
| Component | File | Description | Key Primitives Used |
|---|---|---|---|
| AppSidebar | app-sidebar.tsx |
Main navigation sidebar with collapsible sections | Sidebar, Collapsible, Tooltip |
| AppHeader | app-header.tsx |
Top header bar with search, notifications, and user menu | Input, Avatar, DropdownMenu |
| ThemeToggle | theme-toggle.tsx |
Light/dark mode toggle button | Button |
| SkipLink | skip-link.tsx |
Accessibility skip navigation link | -- |
Accessibility Composites (components/a11y/)
| Component | File | Description |
|---|---|---|
| LiveRegion | live-region.tsx |
ARIA live region for dynamic content announcements |
| VisuallyHidden | visually-hidden.tsx |
Content visible to screen readers only |
Tier 3: Pages
Pages are the top-level compositions in app/(app)/. Each page file:
- Imports seed data from
data/seed.ts - Imports composites and primitives as needed
- Applies page-specific formatting with
lib/format.ts - Renders a complete screen with proper layout and responsive design
Example: Dashboard Page
// app/(app)/dashboard/page.tsx
import { PageHeader } from "@/components/shared/page-header"
import { StatCard } from "@/components/dashboard/stat-card"
import { EngagementChart } from "@/components/dashboard/engagement-chart"
import { EngagementDistribution } from "@/components/dashboard/engagement-distribution"
import { RecentCampaigns } from "@/components/dashboard/recent-campaigns"
import { TopAutomations } from "@/components/dashboard/top-automations"
import {
dashboardMetrics,
subscriberGrowthData,
engagementDistribution,
campaigns,
automations,
} from "@/data/seed"
Complex Pages (Inline Compositions)
Some screens like the email editor (/campaigns/builder) and flow builder (/automations/[id]) contain significant inline logic rather than extracting everything into separate composites. This is intentional -- these are complex, single-use UIs where extracting every piece would add indirection without reuse benefit.
Adding New Composites
When building new composites, follow these conventions:
- Location -- place in
components/{domain}/based on the functional area - Naming -- use kebab-case file names and PascalCase component names
- Props -- define a typed props interface (no
any) - Imports -- use
@/path alias for all imports - Primitives -- build from Tier 1 components; do not duplicate primitive behavior
// components/campaigns/campaign-status-card.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { StatusBadge } from "@/components/shared/status-badge"
import type { Campaign } from "@/types"
interface CampaignStatusCardProps {
campaign: Campaign
}
export function CampaignStatusCard({ campaign }: CampaignStatusCardProps) {
return (
<Card>
<CardHeader>
<CardTitle>{campaign.name}</CardTitle>
</CardHeader>
<CardContent>
<StatusBadge status={campaign.status} />
</CardContent>
</Card>
)
}
Related Docs
- Project Structure -- directory layout
- Design Tokens -- color and typography tokens
- Customization -- extend components
- Accessibility -- accessible component patterns