Project Structure
Last updated on 2026-08-27
The Email Marketing Kit follows Next.js App Router conventions with a clear separation between routes, components, data, and types.
Top-Level Directory Tree
email-marketing-kit/
├── app/
│ ├── (app)/ # Route group: all app pages
│ │ ├── dashboard/
│ │ │ └── page.tsx # Dashboard overview
│ │ ├── campaigns/
│ │ │ ├── page.tsx # Campaign list
│ │ │ ├── [id]/
│ │ │ │ └── page.tsx # Campaign detail
│ │ │ ├── builder/
│ │ │ │ ├── page.tsx # Block-based email editor
│ │ │ │ ├── audience/
│ │ │ │ │ └── page.tsx # Audience picker
│ │ │ │ ├── subject/
│ │ │ │ │ └── page.tsx # Subject & preheader editor
│ │ │ │ └── schedule/
│ │ │ │ └── page.tsx # Send scheduling
│ │ │ ├── templates/
│ │ │ │ └── page.tsx # Email template gallery
│ │ │ ├── preview/
│ │ │ │ └── page.tsx # Email preview mode
│ │ │ └── ab-test/
│ │ │ └── page.tsx # A/B test setup
│ │ ├── automations/
│ │ │ ├── page.tsx # Automation list
│ │ │ ├── [id]/
│ │ │ │ ├── page.tsx # Flow builder canvas
│ │ │ │ └── analytics/
│ │ │ │ └── page.tsx # Automation analytics
│ │ │ ├── templates/
│ │ │ │ └── page.tsx # Automation templates
│ │ │ └── activity/
│ │ │ └── page.tsx # Activity log
│ │ ├── audience/
│ │ │ ├── subscribers/
│ │ │ │ ├── page.tsx # Subscriber list
│ │ │ │ └── [id]/
│ │ │ │ └── page.tsx # Subscriber profile
│ │ │ ├── segments/
│ │ │ │ ├── page.tsx # Segment list
│ │ │ │ └── builder/
│ │ │ │ └── page.tsx # Segment rule builder
│ │ │ ├── tags/
│ │ │ │ └── page.tsx # Tag manager
│ │ │ ├── lists/
│ │ │ │ └── page.tsx # Mailing list management
│ │ │ ├── forms/
│ │ │ │ └── page.tsx # Growth tools & forms
│ │ │ ├── import/
│ │ │ │ └── page.tsx # Import subscribers
│ │ │ └── suppressions/
│ │ │ └── page.tsx # Suppression list
│ │ ├── analytics/
│ │ │ ├── page.tsx # Analytics overview
│ │ │ ├── campaigns/
│ │ │ │ └── page.tsx # Campaign analytics
│ │ │ ├── audience/
│ │ │ │ └── page.tsx # Audience growth analytics
│ │ │ ├── revenue/
│ │ │ │ └── page.tsx # Revenue tracking
│ │ │ ├── click-map/
│ │ │ │ └── page.tsx # Click map overlay
│ │ │ ├── deliverability/
│ │ │ │ └── page.tsx # Deliverability dashboard
│ │ │ ├── domains/
│ │ │ │ └── page.tsx # Domain authentication
│ │ │ └── bounces/
│ │ │ └── page.tsx # Bounce management
│ │ ├── settings/
│ │ │ ├── page.tsx # General settings
│ │ │ ├── team/
│ │ │ │ └── page.tsx # Team management
│ │ │ ├── api/
│ │ │ │ └── page.tsx # API keys & integrations
│ │ │ └── billing/
│ │ │ └── page.tsx # Billing & invoices
│ │ ├── notifications/
│ │ │ └── page.tsx # Notification center
│ │ ├── layout.tsx # Shared sidebar + header
│ │ └── page.tsx # Redirect to /dashboard
│ ├── globals.css # Design tokens & base styles
│ ├── layout.tsx # Root layout (fonts, theme provider)
│ └── page.tsx # Landing redirect
├── components/
│ ├── ui/ # Tier 1: shadcn/ui primitives
│ ├── dashboard/ # Tier 2: dashboard composites
│ ├── shared/ # Tier 2: reusable composites
│ ├── layout/ # Tier 2: layout composites
│ └── a11y/ # Tier 2: accessibility helpers
├── data/
│ └── seed.ts # All mock data
├── types/
│ └── index.ts # TypeScript interfaces
├── lib/
│ ├── format.ts # Formatting utilities
│ └── utils.ts # General utilities (cn)
├── public/ # Static assets
├── package.json
├── tsconfig.json
└── next.config.ts
Route Group: (app)
All application pages live inside the (app) route group, which wraps pages with a shared layout containing the sidebar navigation and top header bar. The parentheses in (app) tell Next.js this is a route group -- it does not appear in the URL.
Components Directory
Components follow a 3-tier hierarchy:
Tier 1: Primitives (components/ui/)
These are shadcn/ui components installed via the shadcn CLI. They should never be modified directly.
| Component | File |
|---|---|
| Alert Dialog | alert-dialog.tsx |
| Avatar | avatar.tsx |
| Badge | badge.tsx |
| Breadcrumb | breadcrumb.tsx |
| Button | button.tsx |
| Card | card.tsx |
| Chart | chart.tsx |
| Checkbox | checkbox.tsx |
| Collapsible | collapsible.tsx |
| Command | command.tsx |
| Dialog | dialog.tsx |
| Dropdown Menu | dropdown-menu.tsx |
| Input | input.tsx |
| Input Group | input-group.tsx |
| Label | label.tsx |
| Popover | popover.tsx |
| Progress | progress.tsx |
| Radio Group | radio-group.tsx |
| Resizable | resizable.tsx |
| Scroll Area | scroll-area.tsx |
| Select | select.tsx |
| Separator | separator.tsx |
| Sheet | sheet.tsx |
| Sidebar | sidebar.tsx |
| Skeleton | skeleton.tsx |
| Slider | slider.tsx |
| Sonner (Toasts) | sonner.tsx |
| Switch | switch.tsx |
| Table | table.tsx |
| Tabs | tabs.tsx |
| Textarea | textarea.tsx |
| Toggle | toggle.tsx |
| Toggle Group | toggle-group.tsx |
| Tooltip | tooltip.tsx |
Tier 2: Composites
Domain-specific components built from Tier 1 primitives.
| Directory | Components | Purpose |
|---|---|---|
components/dashboard/ |
stat-card, engagement-chart, engagement-distribution, recent-campaigns, top-automations |
Dashboard widgets |
components/shared/ |
page-header, data-table, status-badge, empty-state |
Reusable across sections |
components/layout/ |
app-sidebar, app-header, theme-toggle, skip-link |
App chrome and layout |
components/a11y/ |
live-region, visually-hidden |
Accessibility utilities |
Tier 3: Pages
Page files in app/(app)/ wire composites together with seed data. Each page is a self-contained screen that imports composites and data directly.
Data Layer
data/seed.ts
All mock data lives in a single file with named exports. The data includes:
campaigns-- 12+ campaign records with statssubscribers-- 15+ subscriber records with engagement datasegments-- segment definitions with rulestags-- tag definitions with subscriber countssubscriberLists-- mailing list recordsautomations-- automation records with triggersflowNodes-- automation flow node positions and configsautomationTemplates-- pre-built automation templatesemailTemplates-- email template definitionsemailBlocks-- content block definitionsdashboardMetrics-- KPI datasubscriberGrowthData-- time-series growth datadeliverabilityMetrics-- deliverability scoresdomainAuth-- domain authentication recordsbounceEntries-- bounce log datateamMembers-- team member recordsapiKeys-- API key recordsintegrations-- integration recordsbillingPlan-- billing plan configurationinvoices-- invoice historygrowthForms-- signup form datasuppressionEntries-- suppression listrevenueData-- revenue time-series datanavigationItems-- sidebar navigation structure
types/index.ts
All TypeScript interfaces are defined in a single file, organized by domain:
- Campaigns:
Campaign,CampaignStats,CampaignVariant,EmailBlock,EmailBlockStyles,EmailTemplate,EmailTemplateGlobalStyles - Audience:
Subscriber,Segment,SegmentRule,Tag,SubscriberList,SubscriberListStats,ImportJob,SuppressionEntry,GrowthForm - Automations:
Automation,FlowNode,FlowNodePosition,FlowTrigger,AutomationTemplate - Analytics:
DashboardMetrics,SubscriberGrowthData,CampaignPerformanceData,RevenueData,EngagementDistribution,TopClickedLink,DeviceBreakdown,GeoData,DeliverabilityMetrics,DomainAuth,DnsRecord,AuthStatus,IspBreakdownItem,BounceEntry - Settings:
TeamMember,ApiKey,Integration,BillingPlan,BillingPlanLimits,BillingPlanUsage,Invoice - Shared:
NavItem,NavigationItem,ChartDataPoint,TimeRange
lib/format.ts
Pure formatting functions using native Intl APIs -- no external dependencies:
| Function | Example |
|---|---|
formatCurrency(cents) |
9999 -> $99.99 |
formatCompact(n) |
1200 -> 1.2K |
formatPercent(n) |
0.245 -> 24.5% |
formatDate(date) |
"2026-08-20T10:00:00Z" -> Aug 20, 2026 |
formatRelativeDate(date) |
"2026-08-25T10:00:00Z" -> 2 days ago |
formatNumber(n) |
1234567 -> 1,234,567 |
formatStatus(status) |
"in-progress" -> In Progress |
truncate(str, max) |
truncate("Hello World", 8) -> Hello... |