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:

  1. Imports seed data from data/seed.ts
  2. Imports composites and primitives as needed
  3. Applies page-specific formatting with lib/format.ts
  4. 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:

  1. Location -- place in components/{domain}/ based on the functional area
  2. Naming -- use kebab-case file names and PascalCase component names
  3. Props -- define a typed props interface (no any)
  4. Imports -- use @/ path alias for all imports
  5. 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>
  )
}