Customization
Last updated on 2026-03-22
The Blog CMS Kit is designed to be customized for your blog or publication. 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 amber theme uses oklch hue 55. Change the hue to rebrand the entire kit:
:root {
/* Change from amber (hue 55) to blue (hue 250) */
--primary: oklch(0.45 0.2 250);
--primary-foreground: oklch(0.98 0 0);
--accent: oklch(0.94 0.02 250);
--accent-foreground: oklch(0.35 0.15 250);
--ring: oklch(0.55 0.18 250);
}
.dark {
--primary: oklch(0.7 0.18 250);
--primary-foreground: oklch(0.15 0.02 250);
}
All 25 screens automatically inherit the new colors -- buttons, badges, links, charts, sidebar accents, 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:
Static data
import { getPosts } from "@/lib/api"
export default async function BlogHome() {
const posts = await getPosts()
return <PostGrid posts={posts} />
}
CMS integration (Sanity, Contentful)
import { sanityClient } from "@/lib/sanity"
const posts = await sanityClient.fetch(`*[_type == "post"] | order(publishedAt desc)`)
Database (Prisma, Drizzle)
import { db } from "@/lib/db"
const posts = await db.post.findMany({
orderBy: { publishedAt: "desc" },
include: { author: 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">
Subscribe
</Button>
Adding Component Variants
Components use class-variance-authority for variant management:
const buttonVariants = cva("...", {
variants: {
variant: {
default: "...",
brand: "bg-brand-500 text-white hover:bg-brand-600",
},
},
})
Customizing the Blog Header
Edit components/layout/blog-header.tsx to:
- Add or remove navigation links -- update the
navLinksarray - Change the logo -- replace the "B" icon and "BlogCMS" text with your brand
- Add/remove header actions -- the search, RSS, and theme toggle buttons are individually removable
const navLinks = [
{ label: "Home", href: "/" },
{ label: "Categories", href: "/categories/technology" },
{ label: "About", href: "/about" },
{ label: "Newsletter", href: "/newsletter" },
// Add your links here
]
Customizing the Admin Sidebar
Edit components/layout/admin-sidebar.tsx to add or remove navigation items:
const navItems = [
{ label: "Dashboard", href: "/admin", icon: LayoutDashboard },
{ label: "Posts", href: "/admin/posts", icon: FileText },
{ label: "Media", href: "/admin/media", icon: Image },
// Remove items you don't need
// Add items for new admin pages
]
Each item takes a label, href, and a Lucide icon component. Active state highlighting is handled automatically based on the current pathname.
Adding New Blog Pages
- Create a new route in
app/(blog)/your-page/page.tsx - The page automatically inherits the blog layout (header, footer)
- Add a navigation link in
components/layout/blog-header.tsx
// app/(blog)/resources/page.tsx
export default function ResourcesPage() {
return (
<main id="main-content" className="mx-auto max-w-6xl px-4 py-12">
<h1 className="font-heading text-3xl font-bold">Resources</h1>
{/* Your content */}
</main>
)
}
Adding New Admin Pages
- Create a new page in
app/(admin)/admin/your-page/page.tsx - Add a sidebar link in
components/layout/admin-sidebar.tsx - Create any needed composites in
components/admin/
// app/(admin)/admin/seo/page.tsx
export default function SEOPage() {
return (
<div className="space-y-6">
<h1 className="font-heading text-2xl font-bold">SEO Settings</h1>
{/* Your content */}
</div>
)
}
Integrating a Rich Text Editor
The kit includes a post editor page at app/(admin)/admin/posts/new/page.tsx with a placeholder textarea. To add a full rich text editor:
Novel (recommended)
pnpm add novel
import { Editor } from "novel"
<Editor
defaultValue={post.content}
onUpdate={(editor) => setContent(editor.getHTML())}
/>
Tiptap
pnpm add @tiptap/react @tiptap/starter-kit @tiptap/extension-image
import { useEditor, EditorContent } from "@tiptap/react"
import StarterKit from "@tiptap/starter-kit"
const editor = useEditor({ extensions: [StarterKit] })
<EditorContent editor={editor} />
Integrating a Real Comment System
The kit renders comments from seed data. Replace with a live comment system:
Giscus (GitHub Discussions)
pnpm add @giscus/react
import Giscus from "@giscus/react"
<Giscus
repo="your-org/your-repo"
repoId="..."
category="Blog Comments"
categoryId="..."
mapping="pathname"
theme="preferred_color_scheme"
/>
Disqus
pnpm add disqus-react
import { DiscussionEmbed } from "disqus-react"
<DiscussionEmbed shortname="your-shortname" config={{ url, identifier, title }} />
Integrating Newsletter Providers
The NewsletterForm component in components/blog/newsletter-form.tsx currently handles email collection on the client. Wire it to your email provider:
Resend
// app/api/subscribe/route.ts
import { Resend } from "resend"
const resend = new Resend(process.env.RESEND_API_KEY)
export async function POST(request: Request) {
const { email } = await request.json()
await resend.contacts.create({ email, audienceId: "..." })
return Response.json({ success: true })
}
ConvertKit
await fetch(`https://api.convertkit.com/v3/forms/${formId}/subscribe`, {
method: "POST",
body: JSON.stringify({ api_key: process.env.CONVERTKIT_API_KEY, email }),
})
Mailchimp
await fetch(`https://${dc}.api.mailchimp.com/3.0/lists/${listId}/members`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.MAILCHIMP_API_KEY}` },
body: JSON.stringify({ email_address: email, status: "subscribed" }),
})
Removing Unused Features
Delete directories you don't need:
- Don't need the newsletter? Delete
app/(blog)/newsletter/andcomponents/blog/newsletter-form.tsx - Don't need comments? Delete
app/(admin)/admin/comments/andcomponents/blog/comment-card.tsx - Don't need analytics? Delete
app/(admin)/admin/analytics/andcomponents/admin/analytics-chart.tsx - Don't need scheduled posts? Delete
app/(admin)/admin/scheduled/ - 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.