Development

How to Create a Brand Kit That Actually Works in Code (React, Next.js, Tailwind)

SVG Genie TeamSVG Design Expert & Technical Writer at SVG Genie
||13 min read

Reviewed by SVG Genie Editorial Team

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/font with 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:

  • currentColor inheritance -- 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-label and role="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.

Try SVG Genie Freearrow_forward

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

Ready to Create Your Own Vectors?

Start designing with AI-powered precision today.

Get Started Freearrow_forward