Your designer sends over the brand guide. It is a 24-page PDF with Pantone swatches, CMYK breakdowns, and a "minimum clear space" diagram. Very professional. Completely useless in your Next.js codebase.
You need hex codes, not Pantone numbers. You need a React component, not a logo PNG at 300 DPI. You need CSS variables that flip for dark mode, not a "dark background version" exported as a separate file. You need Tailwind classes, not "Brand Blue: see page 7."
This guide bridges that gap. We will take the brand assets designers deliver and turn them into a developer brand kit that lives in your codebase, is enforced by your type system, and works across every context your product ships in.
What Developers Actually Need from a Brand Kit
A developer brand guide is not a PDF. It is a set of files and configurations:
- SVG logo as a React component with props for size, color, and className -- not a PNG import
- Brand colors as CSS variables mapped to Tailwind theme tokens, with dark mode variants
- Favicon and app icon properly configured in the Next.js app directory
- OG image meta tags generated dynamically from your brand assets
- Dark mode color switching that works automatically via CSS, not JavaScript toggles
- Typography loaded via
next/fontwith proper font-display and subsetting
If your brand kit does not include all six, it is incomplete. Let us build each one.
Step 1: Your Logo as a React Component
Most codebases import logos like this:
import Image from "next/image";
// This works, but you lose all CSS control
<Image src="/logo.png" alt="Acme" width={120} height={32} />
The PNG approach means you cannot change the logo color for dark mode, you cannot animate it, and you are shipping a raster file that looks blurry on high-DPI screens at certain sizes. The next/image approach with an SVG file is slightly better, but you still lose CSS control because the SVG is loaded as an external resource.
The right approach is an inline SVG wrapped in a React component:
// components/logo.tsx
interface LogoProps {
className?: string;
color?: string;
width?: number;
height?: number;
}
export function Logo({
className,
color = "currentColor",
width = 120,
height = 32,
}: LogoProps) {
return (
<svg
viewBox="0 0 120 32"
width={width}
height={height}
className={className}
fill={color}
aria-label="Acme logo"
role="img"
>
{/* Paste your SVG paths here */}
<path d="M10 2L2 28h8l2-6h12l2 6h8L26 2H10zm8 14h-4l4-10 4 10h-4z" />
<path d="M50 2v26h8V18l8 10h10L66 16l10-14H66l-8 10V2h-8z" />
</svg>
);
}
This gives you:
currentColorinheritance -- the logo matches whatever text color surrounds it, including dark mode flips- CSS control via
className-- apply Tailwind utilities directly (className="w-24 text-brand-primary") - TypeScript props for size and color overrides
- Accessibility with
aria-labelandrole="img" - Zero HTTP requests -- the SVG is inlined in your component tree
Create a second component for your icon mark (the standalone symbol without text), which you will use for favicons and small contexts:
// components/logo-mark.tsx
export function LogoMark({
className,
color = "currentColor",
size = 32,
}: {
className?: string;
color?: string;
size?: number;
}) {
return (
<svg
viewBox="0 0 32 32"
width={size}
height={size}
className={className}
fill={color}
aria-label="Acme"
role="img"
>
<path d="M16 2L2 28h8l2-6h12l2 6h8L16 2zm0 8l4 10h-8l4-10z" />
</svg>
);
}
If you are starting from a raw SVG file and need to convert it to a clean React component, SVG Genie's SVG to React converter handles the JSX attribute conversion (class to className, stroke-width to strokeWidth, etc.) and strips unnecessary metadata automatically. You can also clean up the SVG paths first with the SVG optimizer to reduce the markup.
Step 2: Brand Colors in Tailwind
Hard-coding hex values across your codebase is a maintenance disaster. The correct approach is a single source of truth using CSS custom properties, mapped into your Tailwind config.
First, define your brand colors as CSS variables with dark mode variants:
/* app/globals.css */
@layer base {
:root {
--brand-primary: #2563eb;
--brand-secondary: #1e293b;
--brand-accent: #06b6d4;
--brand-muted: #64748b;
--brand-surface: #ffffff;
--brand-surface-elevated: #f8fafc;
--brand-border: #e2e8f0;
}
.dark {
--brand-primary: #60a5fa;
--brand-secondary: #e2e8f0;
--brand-accent: #22d3ee;
--brand-muted: #94a3b8;
--brand-surface: #0f172a;
--brand-surface-elevated: #1e293b;
--brand-border: #334155;
}
}
Then extend your Tailwind config to reference these variables:
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
darkMode: "class",
content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"],
theme: {
extend: {
colors: {
brand: {
primary: "var(--brand-primary)",
secondary: "var(--brand-secondary)",
accent: "var(--brand-accent)",
muted: "var(--brand-muted)",
surface: "var(--brand-surface)",
"surface-elevated": "var(--brand-surface-elevated)",
border: "var(--brand-border)",
},
},
},
},
};
export default config;
Now you can use bg-brand-primary, text-brand-secondary, border-brand-border throughout your app. When dark mode toggles, every usage updates automatically. No conditional classes, no dark:text-* overrides scattered across files.
One important detail: define your colors with the CSS variable approach rather than Tailwind's darkMode color pairs. This way, any third-party component or inline style that references var(--brand-primary) also respects dark mode -- not just Tailwind utility classes.
Step 3: Favicon and App Icon Setup
Next.js 13+ (app directory) supports a file-convention approach for metadata icons. Drop the right files in your app/ directory and they are picked up automatically:
app/
favicon.ico # Classic favicon (16x16 and 32x32)
icon.svg # Modern browsers use SVG favicons
apple-icon.png # Apple touch icon (180x180)
For the SVG favicon, you can use a simplified version of your icon mark:
<!-- app/icon.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path fill="#2563eb" d="M16 2L2 28h8l2-6h12l2 6h8L16 2zm0 8l4 10h-8l4-10z"/>
</svg>
SVG favicons have a superpower: they support prefers-color-scheme media queries, so your favicon can adapt to the user's system dark mode:
<!-- app/icon.svg with dark mode support -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<style>
path { fill: #2563eb; }
@media (prefers-color-scheme: dark) {
path { fill: #60a5fa; }
}
</style>
<path d="M16 2L2 28h8l2-6h12l2 6h8L16 2zm0 8l4 10h-8l4-10z"/>
</svg>
For the .ico fallback, you need a proper multi-resolution ICO file (16x16 + 32x32). You can generate this from your SVG icon mark using any SVG-to-ICO converter, or use Next.js's dynamic icon.tsx approach:
// app/icon.tsx
import { ImageResponse } from "next/og";
export const size = { width: 32, height: 32 };
export const contentType = "image/png";
export default function Icon() {
return new ImageResponse(
(
<div
style={{
width: 32,
height: 32,
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "#2563eb",
borderRadius: 6,
color: "#fff",
fontSize: 20,
fontWeight: 700,
}}
>
A
</div>
),
{ ...size }
);
}
Step 4: OG Images with next/og
Link previews on Twitter, LinkedIn, Slack, and iMessage are the most underrated brand touchpoint. Most developers skip this, which means their links show up as generic text. Dynamic OG images fix that.
Next.js provides the ImageResponse API (built on @vercel/og) for generating images at the edge:
// app/og/route.tsx
import { ImageResponse } from "next/og";
export const runtime = "edge";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const title = searchParams.get("title") ?? "Acme";
return new ImageResponse(
(
<div
style={{
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
padding: 60,
background: "#0f172a",
color: "#fff",
fontFamily: "Inter, sans-serif",
}}
>
{/* Inline your SVG logo mark */}
<svg viewBox="0 0 32 32" width={48} height={48} fill="#60a5fa">
<path d="M16 2L2 28h8l2-6h12l2 6h8L16 2zm0 8l4 10h-8l4-10z" />
</svg>
<div style={{ fontSize: 48, fontWeight: 700, marginTop: 24 }}>
{title}
</div>
<div style={{ fontSize: 24, color: "#94a3b8", marginTop: 12 }}>
acme.com
</div>
</div>
),
{ width: 1200, height: 630 }
);
}
Then reference it in your layout or page metadata:
// app/layout.tsx
export const metadata = {
openGraph: {
images: [
{
url: "/og?title=Acme%20-%20Build%20Faster",
width: 1200,
height: 630,
},
],
},
};
Notice that the OG image uses the same brand colors and logo mark as the rest of your app. Because the logo is an inline SVG, you can embed it directly in the ImageResponse -- no need to fetch an external image file.
Step 5: Typography with next/font
Font loading is a solved problem in Next.js, but most developers still do it wrong. The next/font module handles subsetting, self-hosting, and font-display: swap automatically:
// app/layout.tsx
import { Inter, JetBrains_Mono } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
variable: "--font-sans",
display: "swap",
});
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-mono",
display: "swap",
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
<body className="font-sans antialiased">{children}</body>
</html>
);
}
Map the CSS variables in your Tailwind config:
// tailwind.config.ts (add to theme.extend)
fontFamily: {
sans: ["var(--font-sans)", "system-ui", "sans-serif"],
mono: ["var(--font-mono)", "monospace"],
},
Two common mistakes to avoid: loading fonts from Google Fonts via a <link> tag (blocks rendering and leaks data to Google) and not specifying the subsets option (ships the entire font file instead of just Latin characters).
Step 6: Dark Mode That Actually Works
If you followed the CSS variable approach from Step 2, dark mode for your brand colors is already handled. But there are three more pieces most developers miss.
SVG logos with currentColor:
Because we set fill={color} with a default of "currentColor" in Step 1, the logo inherits the parent's text color. In dark mode, if the parent text flips to white, the logo flips too. Zero extra code.
{/* Light mode: renders in dark text. Dark mode: renders in light text. */}
<header className="text-slate-900 dark:text-slate-100">
<Logo className="h-8 w-auto" />
</header>
SVG illustrations and complex graphics:
For multi-color SVGs where currentColor is not enough, use CSS variables directly in your SVG paths:
export function BrandIllustration({ className }: { className?: string }) {
return (
<svg viewBox="0 0 200 200" className={className}>
<circle cx="100" cy="100" r="80" fill="var(--brand-primary)" />
<rect x="60" y="60" width="80" height="80" fill="var(--brand-surface)" />
<path d="M90 80l30 20-30 20z" fill="var(--brand-accent)" />
</svg>
);
}
Because the CSS variables already have dark mode definitions, this SVG automatically adapts. No conditional rendering, no duplicate SVG files.
System preference detection:
Use next-themes or a manual approach to sync the dark class with the user's system preference:
// app/providers.tsx
"use client";
import { ThemeProvider } from "next-themes";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
);
}
The key insight: if your entire brand kit is built on CSS variables and currentColor, dark mode support is not a separate feature -- it is a natural consequence of the architecture.
Where to Get Your SVG Assets
Everything in this guide assumes you have clean SVG source files. Not PNGs. Not JPGs. Not Figma frames exported at 2x.
If you are starting from scratch, you need:
- A primary logo as SVG (wordmark + icon)
- A standalone icon mark as SVG (for favicons and small contexts)
- A monochrome version (for OG images and contexts where color is limited)
SVG Genie's Brand Kit generates all of these variants as production-ready SVG files -- not raster images. The AI SVG generator can create individual assets if you need custom icons or illustrations that match your brand style.
Already have SVG files that need cleanup? The SVG editor lets you adjust paths and colors visually, and the SVG optimizer strips the metadata bloat that design tools leave behind.
For converting existing SVG files into React components with proper TypeScript types, the SVG to React converter automates the tedious attribute renaming.
The Complete Brand Config
Centralize all your brand tokens in a single TypeScript file that serves as the developer-facing source of truth:
// lib/brand.config.ts
export const brand = {
name: "Acme",
tagline: "Build faster, ship sooner",
url: "https://acme.com",
colors: {
light: {
primary: "#2563eb",
secondary: "#1e293b",
accent: "#06b6d4",
muted: "#64748b",
surface: "#ffffff",
surfaceElevated: "#f8fafc",
border: "#e2e8f0",
},
dark: {
primary: "#60a5fa",
secondary: "#e2e8f0",
accent: "#22d3ee",
muted: "#94a3b8",
surface: "#0f172a",
surfaceElevated: "#1e293b",
border: "#334155",
},
},
fonts: {
sans: "Inter",
mono: "JetBrains Mono",
},
og: {
width: 1200,
height: 630,
defaultTitle: "Acme - Build Faster",
},
social: {
twitter: "@acme",
github: "acme",
},
} as const;
export type BrandColors = typeof brand.colors.light;
This file becomes the reference point for every brand decision in your codebase. Your CSS variable definitions, Tailwind config, OG image generator, and metadata all derive from this single object. When the brand evolves, you update one file.
You can even generate your CSS variables from this config at build time:
// scripts/generate-brand-css.ts
import { brand } from "../lib/brand.config";
function generateCSS() {
const lightVars = Object.entries(brand.colors.light)
.map(([key, value]) => {
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
return ` --brand-${cssKey}: ${value};`;
})
.join("\n");
const darkVars = Object.entries(brand.colors.dark)
.map(([key, value]) => {
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
return ` --brand-${cssKey}: ${value};`;
})
.join("\n");
return `:root {\n${lightVars}\n}\n\n.dark {\n${darkVars}\n}`;
}
console.log(generateCSS());
Putting It All Together
Here is the file structure for a brand kit that lives in code:
project/
lib/
brand.config.ts # Single source of truth
components/
logo.tsx # Full logo (wordmark + icon)
logo-mark.tsx # Icon mark only
brand-illustration.tsx # Complex multi-color SVG
app/
globals.css # CSS variables (light + dark)
layout.tsx # Font loading + metadata
icon.svg # SVG favicon (with dark mode)
apple-icon.png # Apple touch icon
og/
route.tsx # Dynamic OG image generator
tailwind.config.ts # Brand color tokens
Every file is version-controlled. Every color is defined once. Every SVG responds to dark mode. The designer's intent is preserved, and the implementation is something you can actually ship.
Get Started
If you are building a brand kit for a new project, start with the SVG source files:
- Brand Kit -- Generate a complete set of SVG brand assets (logo, icon mark, favicon, social) in one workflow
- AI SVG Generator -- Create individual SVG assets with AI, from icons to illustrations
- SVG to React Converter -- Turn any SVG file into a typed React component
For more on working with SVGs in production codebases, see:
Create your own SVG graphics with AI
Describe what you need, get a production-ready vector in seconds. No design skills required.
About This Article
This article was written by SVG Genie Team based on hands-on testing with SVG Genie's tools and years of experience in vector design and web graphics. All recommendations reflect real-world usage and are reviewed by the SVG Genie editorial team for accuracy.
About the Author
SVG Genie Team
SVG Design Expert & Technical Writer at SVG Genie
SVG Genie Team is a vector design specialist and technical writer at SVG Genie with years of hands-on experience in SVG tooling, AI-assisted design workflows, and web graphics optimization. Their work focuses on making professional vector design accessible to everyone.
More articles by SVG Genie Teamarrow_forward