Customization
Last updated on 2026-03-22
The CRM Dashboard Kit is designed to be customized for your sales team or CRM product. All components use design tokens and Tailwind CSS utilities, making brand adaptation straightforward.
Changing Colors
Update CSS custom properties in app/globals.css. The default indigo/violet theme uses oklch hue 270. Change the hue to rebrand the entire kit:
:root {
/* Change from indigo (hue 270) to teal (hue 180) */
--primary: oklch(0.45 0.2 180);
--primary-foreground: oklch(0.98 0 0);
--accent: oklch(0.94 0.02 180);
--accent-foreground: oklch(0.35 0.15 180);
--ring: oklch(0.55 0.18 180);
}
.dark {
--primary: oklch(0.7 0.18 180);
--primary-foreground: oklch(0.15 0.02 180);
}
All 35+ screens automatically inherit the new colors -- buttons, badges, links, charts, sidebar accents, pipeline stages, and focus rings all update at once.
Changing Typography
Configure fonts in app/layout.tsx:
import { Inter, DM_Sans, JetBrains_Mono } from "next/font/google";
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const dmSans = DM_Sans({ subsets: ["latin"], variable: "--font-dm-sans" });
const jetbrainsMono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains" });
Swap these for any Google Font or local font files. The CSS variables --font-sans, --font-heading, and --font-mono control body text, headings, and code respectively.
Replacing Seed Data
All mock data lives in data/seed.ts. Replace it with your data source:
API calls
import { getDeals } from "@/lib/api"
export default async function DealsPage() {
const deals = await getDeals()
return <KanbanBoard deals={deals} />
}
CMS integration (Sanity, Contentful)
import { sanityClient } from "@/lib/sanity"
const contacts = await sanityClient.fetch(`*[_type == "contact"] | order(createdAt desc)`)
Database (Prisma, Drizzle)
import { db } from "@/lib/db"
const deals = await db.deal.findMany({
orderBy: { updatedAt: "desc" },
include: { contact: true, company: true },
})
Extending Components
Use the cn() utility (from lib/utils.ts) to add custom classes without conflicts:
import { Button } from "@/components/ui/button"
<Button className="rounded-full shadow-lg" size="lg">
Close Deal
</Button>
Adding Component Variants
Components use class-variance-authority for variant management:
const badgeVariants = cva("...", {
variants: {
variant: {
default: "...",
won: "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200",
lost: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200",
},
},
})
Customizing the Sidebar
Edit components/layout/app-sidebar.tsx to add or remove navigation items:
const navItems = [
{ label: "Dashboard", href: "/", icon: LayoutDashboard },
{ label: "Contacts", href: "/contacts", icon: Users },
{ label: "Companies", href: "/companies", icon: Building2 },
{ label: "Deals", href: "/deals", icon: Handshake },
{ label: "Tasks", href: "/tasks", icon: CheckSquare },
{ label: "Reports", href: "/reports", icon: BarChart3 },
{ label: "Email", href: "/email", icon: Mail },
// Remove items you don't need
// Add items for new CRM pages
]
Each item takes a label, href, and a Lucide icon component. Active state highlighting is handled automatically based on the current pathname.
Customizing the Header
Edit components/layout/app-header.tsx to modify quick actions in the top bar:
- Command palette trigger -- the search input that opens Cmd+K
- Notification bell -- links to the notifications page
- Theme toggle -- switches between light and dark mode
- User menu -- dropdown with profile, settings, and sign out
Remove or reorder these actions to fit your CRM workflow.
Adding New Dashboard Pages
- Create a new route in
app/(dashboard)/your-page/page.tsx - The page automatically inherits the dashboard layout (sidebar, header)
- Add a sidebar link in
components/layout/app-sidebar.tsx
// app/(dashboard)/leads/page.tsx
export default function LeadsPage() {
return (
<div className="space-y-6">
<h1 className="font-heading text-2xl font-bold">Lead Scoring</h1>
{/* Your content */}
</div>
)
}
Integrating CRM Backends
Salesforce REST API
// app/api/salesforce/contacts/route.ts
export async function GET() {
const tokenRes = await fetch("https://login.salesforce.com/services/oauth2/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.SALESFORCE_CLIENT_ID!,
client_secret: process.env.SALESFORCE_CLIENT_SECRET!,
}),
})
const { access_token, instance_url } = await tokenRes.json()
const res = await fetch(`${instance_url}/services/data/v60.0/query?q=SELECT+Id,Name,Email+FROM+Contact`, {
headers: { Authorization: `Bearer ${access_token}` },
})
const data = await res.json()
return Response.json(data.records)
}
HubSpot API
// app/api/hubspot/contacts/route.ts
export async function GET() {
const res = await fetch("https://api.hubapi.com/crm/v3/objects/contacts?limit=100", {
headers: { Authorization: `Bearer ${process.env.HUBSPOT_ACCESS_TOKEN}` },
})
const data = await res.json()
return Response.json(data.results)
}
Supabase
import { createClient } from "@supabase/supabase-js"
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
const { data: contacts } = await supabase
.from("contacts")
.select("*, company:companies(*)")
.order("created_at", { ascending: false })
Integrating Email Services
Resend
// app/api/email/send/route.ts
import { Resend } from "resend"
const resend = new Resend(process.env.RESEND_API_KEY)
export async function POST(request: Request) {
const { to, subject, html } = await request.json()
const { data, error } = await resend.emails.send({
from: "CRM <crm@yourdomain.com>",
to,
subject,
html,
})
return Response.json({ data, error })
}
SendGrid
import sgMail from "@sendgrid/mail"
sgMail.setApiKey(process.env.SENDGRID_API_KEY!)
await sgMail.send({
to: contact.email,
from: "crm@yourdomain.com",
subject: "Following up on our conversation",
html: emailTemplate,
})
Postmark
import { ServerClient } from "postmark"
const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN!)
await client.sendEmail({
From: "crm@yourdomain.com",
To: contact.email,
Subject: "Deal update",
HtmlBody: emailTemplate,
})
Integrating Calendar Services
Google Calendar API
// app/api/calendar/events/route.ts
import { google } from "googleapis"
const auth = new google.auth.GoogleAuth({
credentials: JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_KEY!),
scopes: ["https://www.googleapis.com/auth/calendar.readonly"],
})
const calendar = google.calendar({ version: "v3", auth })
export async function GET() {
const res = await calendar.events.list({
calendarId: "primary",
timeMin: new Date().toISOString(),
maxResults: 20,
singleEvents: true,
orderBy: "startTime",
})
return Response.json(res.data.items)
}
Cal.com
// app/api/calendar/bookings/route.ts
export async function GET() {
const res = await fetch("https://api.cal.com/v1/bookings?apiKey=" + process.env.CAL_API_KEY)
const data = await res.json()
return Response.json(data.bookings)
}
// Create a booking link for contacts
export async function POST(request: Request) {
const { contactEmail, eventTypeId } = await request.json()
const res = await fetch("https://api.cal.com/v1/bookings", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
apiKey: process.env.CAL_API_KEY,
eventTypeId,
responses: { email: contactEmail },
}),
})
return Response.json(await res.json())
}
Removing Unused Features
Delete directories you don't need:
- Don't need email? Delete
app/(dashboard)/email/andcomponents/crm/email-item.tsx - Don't need reports? Delete
app/(dashboard)/reports/andcomponents/crm/revenue-chart.tsx,deals-chart.tsx,conversion-funnel.tsx,team-radar.tsx,activity-donut.tsx - Don't need tasks? Delete
app/(dashboard)/tasks/andcomponents/crm/task-item.tsx - Don't need onboarding? Delete
app/(dashboard)/onboarding/andcomponents/crm/onboarding-step.tsx - Run
pnpm buildto verify no broken imports
Using shadcn/ui CLI
Add new components alongside the kit:
npx shadcn@latest add <component-name>
Components install to components/ui/ and integrate seamlessly with the existing design tokens. The kit uses the new-york style with CSS variables enabled.