How to Add Multiple Custom Fonts in React with Tailwind CSS v4

Published on: 17th October 2025

How to Add Multiple Custom Fonts in React with Tailwind CSS v4

The way we configure fonts in Tailwind v4 is different from v3. There’s no tailwind.config.ts file anymore; v4 has a new method that makes adding custom fonts much easier.

Typography can completely transform the look of your app, making it appear sleek and professional. Sometimes the font you want is available on Google Fonts, but I still prefer downloading it in WOFF and WOFF2 formats instead of using the @import method.

Why? Because self-hosting fonts gives you four major benefits:

  • Faster loading — No extra DNS lookups or connections to Google’s servers
  • Better caching control — You decide how long browsers store your fonts
  • Reduced render blocking — You can preload fonts so they don’t slow down your page
  • No layout shift — Fonts load with your app, so text doesn’t jump around

Plus, sometimes the Figma design you get from the design team uses fonts that aren’t even available on Google Fonts. In those cases, you have no choice but to host the fonts yourself.

Quick tip: Stick to 2–3 fonts per project at most. More than that, and your site starts feeling messy.

When I first tried setting up custom fonts in React with Tailwind, I thought it would be a pain. All those file paths, theme configurations, and font declarations seemed overwhelming. But once I figured out the right steps, it was actually pretty straightforward.

So in this post, I’ll walk you through exactly how to add multiple custom fonts in React with Tailwind v4 in 3 simple steps in a clean and organized way.

If you’ve ever gotten confused about font setup (like I did), this guide should save you some headaches.

Let’s dive in step by step.

1. Add Your Font Files

Let’s start with the first step, adding your downloaded fonts.

For this tutorial, I’m using two fonts — Anek Malayalam and Poppins. I always keep all my fonts in a dedicated folder so things stay organized and easy to find.

I create mine in src/assets/fonts/ (recommended structure)

Inside the fonts folder, I give each font its own folder to keep things clean:

Project folder in VS Code showing multiple custom font files inside src/assets/fonts/.

Now, why do we use .woff and .woff2?

  • They’re the most modern and optimized formats.
  • Smaller file size = faster loading.
  • Widely supported across all modern browsers.

You may also see .ttf or .otf fonts floating around, but it’s always better to convert them to .woff/.woff2 before using them in production.

2. Register Fonts with @font-face

Now that our font files are sitting neatly in the src/assets/fonts/ folder, the next step is to tell the browser about these fonts using @font-face declarations.

Open your src/index.css (or wherever you have your global CSS file) where you imported Tailwind CSS. We'll add our font declarations inside a @layer base block right after the Tailwind import.

Here’s what mine looks like:

1@import 'tailwindcss'; 2 3@layer base { 4 /* Poppins */ 5 @font-face { 6 font-family: 'Poppins'; 7 font-style: normal; 8 font-weight: 200; 9 font-display: swap; 10 src: url('./assets/fonts/poppins/poppins-v20-latin-200.woff2') format('woff2'), 11 url('./assets/fonts/poppins/poppins-v20-latin-200.woff') format('woff'); 12 } 13 @font-face { 14 font-family: 'Poppins'; 15 font-style: normal; 16 font-weight: 300; 17 font-display: swap; 18 src: url('./assets/fonts/poppins/poppins-v20-latin-300.woff2') format('woff2'), 19 url('./assets/fonts/poppins/poppins-v20-latin-300.woff') format('woff'); 20 } 21 @font-face { 22 font-family: 'Poppins'; 23 font-style: normal; 24 font-weight: 400; 25 font-display: swap; 26 src: url('./assets/fonts/poppins/poppins-v20-latin-regular.woff2') format('woff2'), 27 url('./assets/fonts/poppins/poppins-v20-latin-regular.woff') format('woff'); 28 } 29 /* ... rest of the sizes */ 30 31 /* Anek Malayalam */ 32 @font-face { 33 font-family: 'Anek Malayalam'; 34 font-style: normal; 35 font-weight: 400; 36 src: url('./assets/fonts/anek-malayalam/anek-malayalam-v4-malayalam-400.woff2') format('woff2'), 37 url('./assets/fonts/anek-malayalam/anek-malayalam-v4-malayalam-400.woff') format('woff'); 38 font-display: swap; 39 } 40 @font-face { 41 font-family: 'Anek Malayalam'; 42 font-style: normal; 43 font-weight: 500; 44 src: url('./assets/fonts/anek-malayalam/anek-malayalam-v4-malayalam-500.woff2') format('woff2'), 45 url('./assets/fonts/anek-malayalam/anek-malayalam-v4-malayalam-500.woff') format('woff'); 46 font-display: swap; 47 } 48 @font-face { 49 font-family: 'Anek Malayalam'; 50 font-style: normal; 51 font-weight: 600; 52 src: url('./assets/fonts/anek-malayalam/anek-malayalam-v4-malayalam-600.woff2') format('woff2'), 53 url('./assets/fonts/anek-malayalam/anek-malayalam-v4-malayalam-600.woff') format('woff'); 54 font-display: swap; 55 } 56 /* ... rest of the Anek Malayalam sizes */ 57}

Before we continue, let’s understand what these two important pieces do:

What does @layer base do?

The @layer base is Tailwind's way of organizing CSS. It tells Tailwind "hey, put these styles in the base layer" - which means they'll load before any component or utility styles. Think of it as the foundation layer where you put your global styles like font declarations, CSS resets, and other base styles.

What does @font-face do?

This is a standard CSS rule that registers your custom font with the browser. You’re basically saying:

  • “Here’s a font called ‘Poppins’”
  • “It’s normal style, weight 400”
  • “You can find the font files at these URLs”
  • “Use swap display so text shows up quickly”

Each @font-face declaration registers one specific variant (weight, style) of a font family.

⚠️ Important: Make sure your file paths are correct; otherwise, the fonts won’t load. The paths should be relative to your CSS file location.

3. Add Fonts to Tailwind’s Theme

Alright, now that the browser knows about our fonts. The next step is to make them available in Tailwind utilities.

Now comes the cool part about Tailwind v4, we can extend the theme directly in CSS using a @theme block. No more messing around with config files!

Right below your @layer base block, add this:

1@theme { 2 --default-font-family: 'Poppins', sans-serif; 3 --font-secondary: 'Anek Malayalam', sans-serif; 4}

Here’s what this does:

  • default-font-family - This becomes your default font that replaces Tailwind's built-in font. Every element will use Poppins unless you specify otherwise.
  • font-secondary - This creates a custom font utility you can use with font-secondary class.

Don’t want a default font? Just remove the --default flag and use --font-primary or the name you want instead:

1@theme { 2 --font-primary: 'Poppins', sans-serif; 3 --font-secondary: 'Anek Malayalam', sans-serif; 4}

This way, you can easily switch between fonts in your JSX using simple tailwind utility classes like font-primary and font-secondary. No need to remember long font family names or write custom CSS classes.

Pretty neat, right? Tailwind v4 makes theme customization so much cleaner than the old config file approach.

4. Use Fonts in Your React Components

Once your fonts are registered and added to the Tailwind theme, you can apply them just like any other Tailwind utility class.

Here’s a simple example:

1export default function App() { 2 return ( 3 <div className="p-6 space-y-4"> 4 <h1 className="font-secondary text-4xl font-semibold"> 5 Beautiful Heading with Anek Malayalam ✨ 6 </h1> 7 <p className="text-lg"> 8 This paragraph uses Poppins (our default font). 9 Clean, readable, and professional! 10 </p> 11 <p className="font-secondary text-base"> 12 And this paragraph switches to Anek Malayalam 13 with just one class! 14 </p> 15 </div> 16 ); 17}

What’s happening here:

  • The first heading uses font-secondary (Anek Malayalam)
  • The first paragraph uses no font class, so it gets Poppins (our default)
  • The last paragraph explicitly uses font-secondary to switch to Anek Malayalam

How to Verify if Your Font Is Applied

You can quickly check if your custom fonts are working with these methods:

  • Install the WhatFont Chrome extension and hover over any text to see the active font.
  • Use DevTools → Elements panel to inspect text and confirm the font-family.
  • Open the Network tab and look for .woff/.woff2 files being loaded.

If fonts don’t load, check file paths in your @font-face declarations.

Here are a few things that tripped me up:

  • Fonts not loading? Double-check the path. On Vercel/Netlify, fonts sometimes work better in /public/fonts/.
  • Wrong weight showing? Make sure you have defined the correct font-weight in your @font-face.

The new @theme approach makes font management in tailwind v4 so much easier than the old v3 way. Your app will look more professional and you'll have full control over your typography.

Now go make something beautiful! ✨


Flexy UI Newsletter

Build better and faster UIs.Get the latest Tailwind UI components directly in your inbox. No spam!