Customization
Last updated on 2026-08-27
The Email Marketing Kit is designed to be customized. Every aspect of the UI -- from colors and fonts to data structure and block types -- can be modified to match your product.
Change the Primary Hue
The entire color scheme is driven by a single hue value. To change the accent color, update the hue in app/globals.css:
Step 1: Find the Primary Hue
The primary color uses hue 115 (chartreuse-green):
/* app/globals.css */
--primary: oklch(0.48 0.14 115);
Step 2: Replace the Hue
Change 115 to your desired hue across all tokens that reference it. Here are all the tokens that use the accent hue:
:root {
/* Replace 115 with your hue (e.g., 220 for blue, 350 for red, 280 for purple) */
--primary: oklch(0.48 0.14 YOUR_HUE);
--primary-foreground: oklch(0.98 0.005 YOUR_HUE);
--accent-foreground: oklch(0.35 0.12 YOUR_HUE);
--ring: oklch(0.48 0.14 YOUR_HUE);
--chart-1: oklch(0.48 0.14 YOUR_HUE);
--sidebar-primary: oklch(0.48 0.14 YOUR_HUE);
--sidebar-primary-foreground: oklch(0.98 0.005 YOUR_HUE);
--sidebar-accent-foreground: oklch(0.35 0.12 YOUR_HUE);
--sidebar-ring: oklch(0.48 0.14 YOUR_HUE);
}
.dark {
--primary: oklch(0.72 0.14 YOUR_HUE);
--primary-foreground: oklch(0.13 0.02 YOUR_HUE);
--ring: oklch(0.72 0.14 YOUR_HUE);
--chart-1: oklch(0.72 0.14 YOUR_HUE);
--sidebar-primary: oklch(0.72 0.14 YOUR_HUE);
--sidebar-primary-foreground: oklch(0.13 0.02 YOUR_HUE);
--sidebar-ring: oklch(0.72 0.14 YOUR_HUE);
}
Popular Hue Values
| Hue | Color | Example |
|---|---|---|
| 115 | Chartreuse-green (default) | oklch(0.48 0.14 115) |
| 220 | Blue | oklch(0.48 0.14 220) |
| 280 | Purple | oklch(0.48 0.14 280) |
| 350 | Red | oklch(0.48 0.14 350) |
| 30 | Orange | oklch(0.48 0.14 30) |
| 180 | Teal | oklch(0.48 0.14 180) |
| 150 | Green | oklch(0.48 0.14 150) |
Swap Fonts
Fonts are loaded in app/layout.tsx using next/font/google and applied via CSS variables.
Step 1: Import New Fonts
// app/layout.tsx
import { Poppins, Space_Grotesk, Fira_Code } from "next/font/google"
const headingFont = Space_Grotesk({
variable: "--font-familjen", // Keep the same variable name
subsets: ["latin"],
})
const bodyFont = Poppins({
variable: "--font-inter-tight",
subsets: ["latin"],
weight: ["400", "500", "600"],
})
const monoFont = Fira_Code({
variable: "--font-plex-mono",
subsets: ["latin"],
weight: ["400", "500"],
})
Step 2: Apply to Body
The variable names are already wired to globals.css, so the fonts will apply automatically as long as you reuse the same CSS variable names (--font-familjen, --font-inter-tight, --font-plex-mono).
Alternatively, update the CSS variable mappings in globals.css:
@theme inline {
--font-sans: var(--font-your-body), ui-sans-serif, system-ui, sans-serif;
--font-heading: var(--font-your-heading), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-your-mono), ui-monospace, monospace;
}
Modify Seed Data Structure
All mock data lives in data/seed.ts. To modify the data:
Add New Fields to Existing Types
- Update the interface in
types/index.ts:
// types/index.ts
export interface Campaign {
// ...existing fields
priority: 'high' | 'medium' | 'low'; // New field
}
- Add the field to seed data:
// data/seed.ts
export const campaigns: Campaign[] = [
{
id: "cam_1",
name: "Summer Sale",
// ...existing data
priority: "high", // New field
},
]
- Use the field in your page:
// In any page.tsx
<Badge variant={campaign.priority === 'high' ? 'destructive' : 'default'}>
{campaign.priority}
</Badge>
Add Entirely New Data Sets
- Define the type in
types/index.ts - Export the data array from
data/seed.ts - Import and use in your page files
Add New Email Block Types
The email editor supports custom block types. To add a new block:
Step 1: Extend the Type
// types/index.ts
export interface EmailBlock {
id: string;
type: 'header' | 'hero' | 'footer' | 'text' | 'image' | 'button'
| 'divider' | 'columns' | 'social' | 'video' | 'html'
| 'product' | 'countdown' | 'menu'
| 'testimonial'; // New block type
content?: Record<string, unknown>;
config?: Record<string, unknown>;
styles?: EmailBlockStyles;
}
Step 2: Add Seed Data
// data/seed.ts
export const emailBlocks: EmailBlock[] = [
// ...existing blocks
{
id: "block_testimonial_1",
type: "testimonial",
content: {
quote: "This product changed my workflow completely.",
author: "Jane Smith",
role: "Product Manager",
avatar: "/avatars/jane.jpg",
},
styles: {
backgroundColor: "#f9fafb",
padding: "24px",
borderRadius: "8px",
},
},
]
Step 3: Add Block Rendering
In app/(app)/campaigns/builder/page.tsx, add a case for the new block type in the block rendering logic:
function renderBlock(block: EmailBlock) {
switch (block.type) {
// ...existing cases
case 'testimonial':
return (
<div className="p-6 bg-muted rounded-lg">
<blockquote className="text-lg italic">
"{block.content?.quote}"
</blockquote>
<p className="mt-2 font-medium">{block.content?.author}</p>
<p className="text-sm text-muted-foreground">{block.content?.role}</p>
</div>
)
}
}
Step 4: Add Block to Palette
Add the new block type to the block palette in the editor sidebar:
const blockPalette = [
// ...existing blocks
{ type: 'testimonial', icon: Quote, label: 'Testimonial' },
]
Add New Automation Node Types
Step 1: Extend the Type
// types/index.ts
export interface FlowNode {
type: 'trigger' | 'email' | 'wait' | 'split' | 'webhook'
| 'tag' | 'move-to-list' | 'condition'
| 'sms'; // New node type
// ...rest of interface
}
Step 2: Add Node Rendering
In app/(app)/automations/[id]/page.tsx, add rendering and configuration for the new node type:
function getNodeIcon(type: string) {
switch (type) {
// ...existing cases
case 'sms': return MessageSquare
}
}
function getNodeColor(type: string) {
switch (type) {
// ...existing cases
case 'sms': return 'bg-purple-100 text-purple-700'
}
}
Connect a Real ESP (Email Service Provider)
The kit uses seed data by default. To connect a real email backend:
SendGrid
// lib/email.ts
import sgMail from '@sendgrid/mail'
sgMail.setApiKey(process.env.SENDGRID_API_KEY!)
export async function sendCampaign(campaign: Campaign, recipients: string[]) {
const msg = {
to: recipients,
from: campaign.fromEmail || 'noreply@yourdomain.com',
subject: campaign.subject,
html: renderEmailBlocks(campaign.blocks),
}
return sgMail.sendMultiple(msg)
}
Resend
// lib/email.ts
import { Resend } from 'resend'
const resend = new Resend(process.env.RESEND_API_KEY!)
export async function sendCampaign(campaign: Campaign, recipients: string[]) {
return resend.emails.send({
from: campaign.fromEmail || 'noreply@yourdomain.com',
to: recipients,
subject: campaign.subject,
html: renderEmailBlocks(campaign.blocks),
})
}
AWS SES
// lib/email.ts
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses'
const ses = new SESClient({ region: process.env.AWS_REGION })
export async function sendCampaign(campaign: Campaign, recipients: string[]) {
const command = new SendEmailCommand({
Source: campaign.fromEmail || 'noreply@yourdomain.com',
Destination: { ToAddresses: recipients },
Message: {
Subject: { Data: campaign.subject },
Body: { Html: { Data: renderEmailBlocks(campaign.blocks) } },
},
})
return ses.send(command)
}
Add Authentication
The kit does not include authentication by default. Here are common options:
NextAuth.js
pnpm add next-auth
Wrap the app layout with a session provider and add middleware for protected routes.
Clerk
pnpm add @clerk/nextjs
Add the Clerk provider to app/layout.tsx and use authMiddleware() in middleware.ts.
Supabase Auth
pnpm add @supabase/supabase-js @supabase/ssr
Create a Supabase client and add authentication checks to the (app) layout.
Connect a Database
Replace seed data with real database queries:
Prisma
pnpm add prisma @prisma/client
npx prisma init
Define your schema matching the TypeScript interfaces in types/index.ts, then replace seed data imports with Prisma queries.
Drizzle
pnpm add drizzle-orm drizzle-kit
Supabase
pnpm add @supabase/supabase-js
Migration Strategy
- Keep the seed data as a reference for the expected data shape
- Create database tables matching the interfaces in
types/index.ts - Replace
import { campaigns } from "@/data/seed"with database queries - Use React Server Components for data fetching in page files
Related Docs
- Design Tokens -- token reference
- Components -- component hierarchy
- Project Structure -- directory layout
- Campaigns -- email editor details
- Automations -- flow builder details