CSS Typography

Tailwind CSS에 커스텀 폰트를 추가하는 방법

Updated 2월 24, 2026
Tailwind CSS에 Google Fonts 또는 커스텀 폰트 추가 — font-family 테마 확장, 효율적인 폰트 로딩, Tailwind 타이포그래피 모범 사례.

How to Add Custom Fonts to Tailwind CSS

Tailwind CSS doesn't bundle fonts — it ships with a pragmatic default font-family stack built around system fonts, and it expects you to bring your own custom typefaces. This separation is intentional: font loading is a performance-sensitive concern that Tailwind correctly leaves to the developer's judgment rather than automating away. The process involves two distinct steps: loading the font into the browser (using a <link> tag or @font-face), and registering it in Tailwind's theme so you can reference it with utility classes.

This guide covers the complete workflow from font loading through Tailwind theme configuration to production-ready deployment, including the new configuration syntax introduced in Tailwind CSS v4.

Before Tailwind can use a font, the browser needs to know where to find it. There are two ways to accomplish this, and the right choice depends on whether you're using Google Fonts or a self-hosted file.

Loading from Google Fonts

For Google Fonts, add a <link> tag in your HTML <head>. The recommended approach uses preconnect hints to warm up the connection early:

<head>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link
    href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
    rel="stylesheet"
  />
</head>

The two preconnect hints tell the browser to establish connections to Google's servers as early as possible — before the CSS is even requested. This reduces the total latency of the font load by eliminating the connection setup time from the critical path. Without preconnect, the browser doesn't attempt to connect to fonts.googleapis.com until it parses the <link rel="stylesheet"> tag, adding a full round-trip of latency.

The display=swap query parameter appended to the font URL is equivalent to adding font-display: swap to the @font-face declaration — it ensures text is visible immediately in a fallback font rather than invisible during the download.

If you're using Inter via Google Fonts, the above code loads four weights: 400 (Regular), 500 (Medium), 600 (SemiBold), and 700 (Bold). Requesting only the weights you actually use in your design avoids downloading unnecessary font data.

Loading a Self-Hosted Font with @font-face

For self-hosted fonts — either downloaded web font files or files converted from a typeface purchase — use @font-face declarations in your CSS. In a Tailwind project, the conventional place is your main CSS file, typically src/index.css or global.css, inside the @layer base directive:

@layer base {
  @font-face {
    font-family: "Inter";
    src: url("/fonts/inter-regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
  }

  @font-face {
    font-family: "Inter";
    src: url("/fonts/inter-bold.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
  }
}

Placing @font-face inside @layer base is important for specificity management in Tailwind v3. It ensures the font declarations appear early in the generated CSS output and don't interfere with Tailwind's utility layer ordering. In practice, @font-face declarations don't participate in the cascade the same way rules with selectors do, so the practical impact is minimal, but it's considered best practice.

For the complete @font-face syntax guide including the full list of descriptors and options, see how to use @font-face. For the self-hosting workflow from download to deployment, see how to self-host Google Fonts.


Extending the Tailwind Font Family Theme

Loading the font makes it available to the browser. Extending the Tailwind theme makes it available as a utility class. In Tailwind CSS v3, this is done in tailwind.config.js (or tailwind.config.ts) under the theme.extend.fontFamily key:

// tailwind.config.js
const defaultTheme = require("tailwindcss/defaultTheme");

/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      fontFamily: {
        sans: ["Inter", ...defaultTheme.fontFamily.sans],
        display: ["Poppins", ...defaultTheme.fontFamily.sans],
        mono: ["JetBrains Mono", ...defaultTheme.fontFamily.mono],
      },
    },
  },
};

The spread of defaultTheme.fontFamily.sans appends the full Tailwind default font stack as a fallback. This means if Inter fails to load for any reason, the browser falls back to system-ui, -apple-system, BlinkMacSystemFont, and so on — the same clean system font chain Tailwind uses by default. Always include meaningful fallbacks, not just the custom font name.

By using theme.extend.fontFamily rather than overriding theme.fontFamily, you preserve Tailwind's existing font utilities (font-mono, etc.) while adding new ones alongside them.

If you want your custom font to become the default font-sans — the font applied to body in Tailwind's preflight/reset — this approach works: any font-sans class already applied will pick up Inter as the first option. You can also explicitly set the body font in your base layer:

@layer base {
  body {
    font-family: theme("fontFamily.sans");
  }
}

The theme() function accesses any value from your Tailwind config, making it easy to keep font declarations synchronized between your CSS and your configuration.

Naming Convention

Font family keys in the Tailwind config become the suffix of the generated utility class: fontFamily.sansfont-sans, fontFamily.displayfont-display, fontFamily.bodyfont-body. Choose names that reflect the role of the font in your design system rather than the font's name itself. This means your HTML contains class="font-sans" rather than class="font-inter" — if you later change your sans-serif font, you update one config line rather than hunting through your HTML.


Using Font Classes in Your HTML

Once the font is registered in the theme, use Tailwind's generated utility classes in your markup:

<!-- Apply the default sans font (now Inter) -->
<body class="font-sans">
  <!-- Apply display font for headings -->
  <h1 class="font-display text-4xl font-bold">Welcome</h1>

  <!-- Apply at component level -->
  <nav class="font-sans text-sm font-medium">
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</body>

You can combine font-family classes with font-weight classes freely. font-bold in Tailwind generates font-weight: 700, which will trigger the browser to load the corresponding @font-face variant if you've declared one. If no 700-weight @font-face block exists for your font, the browser will synthesize a fake bold — so ensure your theme font files and your @font-face declarations are aligned.

Poppins and Roboto are common choices for secondary or display fonts in Tailwind projects, often paired with Inter for body text. The font-family property that these utilities generate supports all standard CSS font-family features including quotes around names with spaces and comma-separated fallback chains.


The @tailwindcss/typography Plugin

If your project renders long-form content — blog articles, documentation, markdown outputs — the @tailwindcss/typography plugin is essential. It provides a prose utility class that applies opinionated, well-designed typographic styles to HTML content that you don't fully control (like output from a CMS or markdown renderer). Rather than manually styling every element in rendered markdown — h1, h2, p, code, blockquote, table, ul, ol — the plugin provides a sensible, well-considered starting point that respects proportional spacing and typographic rhythm. More importantly, it integrates with Tailwind's theme system, which means your custom font declarations flow through to prose content automatically when you configure the typography theme extension.

Install the plugin:

npm install -D @tailwindcss/typography

Register it in your config:

// tailwind.config.js
module.exports = {
  plugins: [require("@tailwindcss/typography")],
  theme: {
    extend: {
      typography: ({ theme }) => ({
        DEFAULT: {
          css: {
            fontFamily: theme("fontFamily.sans").join(", "),
            h1: {
              fontFamily: theme("fontFamily.display").join(", "),
              fontWeight: "700",
            },
          },
        },
      }),
    },
  },
};

The typography key in the theme lets you customize the prose styles to use your custom fonts. Here, body text in prose blocks uses fontFamily.sans (Inter) while headings use fontFamily.display. This produces consistent typographic hierarchy throughout all prose content.

Apply it in your HTML:

<article class="prose prose-lg max-w-none">
  <h1>Article Title</h1>
  <p>Article content rendered from markdown...</p>
</article>

The prose-lg modifier adjusts the scale upward from the base size. The plugin includes modifiers for sm, base, lg, xl, and 2xl. Responsive variants work as expected: prose-sm md:prose-base lg:prose-lg scales typography with the viewport.


Tailwind CSS v4 Font Configuration

Tailwind CSS v4, released in 2025, changes the configuration model significantly. Config moved from JavaScript (tailwind.config.js) to CSS, using new at-rules directly in your stylesheet. The @theme directive replaces the JavaScript config object:

/* src/app.css or global.css */

@import "tailwindcss";

@theme {
  --font-sans: "Inter", system-ui, -apple-system, sans-serif;
  --font-display: "Poppins", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "Fira Code", monospace;
}

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

In v4, CSS custom properties defined in @theme become the source of truth for Tailwind's generated utilities. --font-sans generates the font-sans class, --font-display generates font-display, and so on. This is more declarative and requires no build-step configuration file.

The @font-face rule can live anywhere in your CSS that's processed by Tailwind — it doesn't need to be inside @theme or @layer. The important thing is that the font-family name in @font-face exactly matches the font name in your --font-* custom property value.

The v4 CSS-first approach means font configuration is co-located with @font-face declarations in a single CSS file, which many developers find easier to reason about than split JavaScript/CSS configuration.


Performance Tips for Tailwind + Custom Fonts

The intersection of Tailwind's utility-first approach and font loading creates some specific optimization opportunities worth addressing.

Preload your primary font file. Tailwind's CSS is typically inlined or loaded early, but the font files it references aren't downloaded until the browser processes the @font-face rule. Add a <link rel="preload"> for your primary body font in the HTML <head>:

<link
  rel="preload"
  href="/fonts/inter-regular.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

This moves the font download to the earliest possible point in the loading process, reducing the window during which visitors see fallback text. Preload only one or two files — preloading too many defeats the purpose by saturating the browser's download queue.

Purge unused Tailwind utilities. Tailwind's content configuration determines which utility classes are included in the production build. Ensure your font utility classes (font-sans, font-display, etc.) are used in files that Tailwind scans, so they're preserved in the build. Font classes are not special — they follow the same purge rules as any other utility.

Use font-display: swap everywhere. In all your @font-face declarations, font-display: swap ensures text is never invisible. This is critical for Core Web Vitals — FOIT (invisible text) delays the Largest Contentful Paint measurement even when the font is loading quickly. Combined with a fallback font that has similar metrics to your custom font, the visual impact of the swap is minimal.

Prefer variable fonts for multiple weights. If you're loading three or more weights of a typeface in your Tailwind project, a variable font reduces the number of HTTP requests from N files to one. Most major fonts used in Tailwind projects — Inter, Roboto, Poppins — have well-maintained variable font versions. One variable font file, declared with a weight range, replaces four or five separate static font files.

Subset for your character set. If your Tailwind application is English-only or primarily uses basic Latin characters, using Google Fonts' &subset=latin parameter (or subsetting your self-hosted files) can reduce each font file by 50-70%. The type scale and custom properties guide at type scale with CSS custom properties covers how to structure a scalable font configuration that works well alongside Tailwind's spacing and sizing system.

Match your fallback font metrics. When font-display: swap is set, users see the fallback font briefly before the custom font loads. If the fallback font has substantially different metrics — a different x-height, different letter spacing, different word spacing — the text reflows visibly when the swap occurs, generating Cumulative Layout Shift (CLS). You can reduce this significantly by choosing a system font fallback with metrics close to your custom font. Tailwind's default sans-serif stack (system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI") uses different proportions than Inter; for minimal layout shift, you can tune the fallback with size-adjust, ascent-override, and descent-override CSS descriptors in your @font-face fallback declaration. These override descriptors adjust the metrics of the fallback font to match your custom font, reducing or eliminating visible reflow entirely.

Organize font files consistently. In a Tailwind project, a conventional place for self-hosted font files is the public/fonts/ directory (for Next.js, Vite, and similar build tools), which maps directly to /fonts/ root-relative URLs. Grouping fonts by family — public/fonts/inter/, public/fonts/roboto/ — keeps the asset directory organized as the project scales. If your Tailwind project uses Next.js, the next/font module handles Google Fonts loading, subsetting, and @font-face generation automatically, making it the most efficient path for Next.js applications specifically.

Typography Terms

Try These Tools

Fonts Mentioned

Roboto Sans Serif #1

Designed by Christian Robertson for Google's Material Design ecosystem, this neo-grotesque sans-serif is the most widely used typeface on the web and Android. Its dual-nature design balances mechanical precision with natural reading rhythm, making it equally at home in UI labels and long-form text. The variable font supports width and weight axes alongside Cyrillic, Greek, and extended Latin scripts.

The quick brown fox jumps over the lazy dog
Inter Sans Serif #5

Rasmus Andersson spent years refining this neo-grotesque specifically for computer screens, optimizing letter spacing, x-height, and stroke contrast for high readability at small sizes on digital displays. An optical size axis (opsz) lets the font automatically adjust its design for captions versus headlines, while the weight axis covers the full range from thin to black. It has become the de facto choice for dashboards, documentation sites, and developer tools worldwide.

The quick brown fox jumps over the lazy dog
Poppins Sans Serif #7

Developed by the Indian Type Foundry, this geometric sans-serif pairs perfectly circular bowls and uniform stroke widths with native Devanagari support, making it one of the few typefaces that genuinely integrates Latin and Indic scripts at a design level. The precise, modern letterforms project confidence and approachability, making Poppins a favorite for startup landing pages and app interfaces. Available in 18 styles across 9 weights, it offers practical flexibility without a variable font.

The quick brown fox jumps over the lazy dog

Related Articles