Picture this: you spend an hour picking the perfect font on Google Fonts. You preview it, fall in love with it, and then open your WordPress dashboard only to find your theme ships with five fonts you never asked for. The font you want is nowhere in the customizer. You search the plugin directory, install a font plugin, and suddenly your site has a new dependency you have to maintain forever just for one typeface. There is a better way.
Adding custom fonts to WordPress without a plugin takes about 10 minutes once you know the method. You get full control, no third-party plugin to update, and often better performance. This guide walks you through three approaches depending on whether you use a classic theme or a modern block theme.
What You Need Before Starting
Before touching any code, make sure these are in place:
- Access to your theme files via FTP, the WordPress file editor, or a local setup
- A child theme set up if you are using a theme you did not build yourself (changes in parent themes get wiped on updates)
- The font files downloaded or the Google Fonts URL ready to go
- Basic comfort with copying and pasting code snippets
Method 1: Google Fonts via functions.php
This is the most common approach for classic themes. You load the font from Google's CDN and then apply it with CSS. Fast to set up, but it does create an external request on every page load.
Step 1: Get your Google Fonts URL
Go to Google Fonts, select the font family and weights you need, then click the "Get embed code" button. Copy the URL from the <link> tag. It looks something like this:
https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap Step 2: Enqueue the font in functions.php
Open your child theme's functions.php and add this code:
function my_theme_enqueue_fonts() {
wp_enqueue_style(
'google-fonts-inter',
'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap',
array(),
null
);
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_fonts' ); Step 3: Apply the font with CSS
In your child theme's style.css, target the elements you want to update:
body {
font-family: 'Inter', sans-serif;
}
h1, h2, h3, h4 {
font-family: 'Inter', sans-serif;
font-weight: 700;
}
Save both files, reload your site, and check DevTools to confirm the font is loading. You should see a request to fonts.googleapis.com in the network tab.
Method 2: Self-Hosting Fonts with @font-face
Self-hosting means you download the font files and serve them from your own server. This removes the external Google request, improves privacy for your visitors, and often loads faster on repeat visits due to browser caching. According to research from the Google Web.dev team and studies from web performance labs at Stanford University, reducing third-party font requests measurably improves Largest Contentful Paint scores.
Step 1: Download the font files
Use the Google Webfonts Helper to download WOFF2 (and WOFF for fallback) versions of your chosen font. WOFF2 is compressed and supported by all modern browsers. Download only the weights you will actually use.
Step 2: Upload to your theme folder
Create a folder in your child theme at /fonts/ and upload the files there. Your structure should look like:
child-theme/
fonts/
inter-400.woff2
inter-600.woff2
inter-700.woff2 Step 3: Declare @font-face rules in style.css
@font-face {
font-family: 'Inter';
src: url('/wp-content/themes/your-child-theme/fonts/inter-400.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('/wp-content/themes/your-child-theme/fonts/inter-700.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
The font-display: swap property tells the browser to show text immediately in a fallback font while your custom font loads. This prevents the invisible-text flash that hurts Core Web Vitals scores. The U.S. government's 21st Century IDEA Act guidance also recommends font-display: swap as a performance best practice for government websites, and the same logic applies to any site.
Step 4: Apply the font with CSS
Same as Method 1: target your elements in CSS with font-family: 'Inter', sans-serif; and you are done.
Method 3: theme.json for Block Themes
If you are using a block theme (like Twenty Twenty-Four or Twenty Twenty-Five), the cleanest approach is theme.json. You register the font there and the block editor exposes it automatically in the Typography panel. No functions.php needed.
Our guide to mastering theme.json covers the full spec, but here is the font-specific part:
Step 1: Add font families to theme.json
{
"version": 3,
"settings": {
"typography": {
"fontFamilies": [
{
"fontFamily": "'Inter', sans-serif",
"name": "Inter",
"slug": "inter",
"fontFace": [
{
"fontFamily": "Inter",
"fontWeight": "400",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": ["file:./fonts/inter-400.woff2"]
},
{
"fontFamily": "Inter",
"fontWeight": "700",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": ["file:./fonts/inter-700.woff2"]
}
]
}
]
}
}
} Step 2: Set the font as default
To make Inter the default body font, add a styles section to theme.json:
"styles": {
"typography": {
"fontFamily": "var(--wp--preset--font-family--inter)"
}
} WordPress generates a CSS custom property for each registered font automatically. This approach keeps font declarations inside your theme bundle and makes them available in the Site Editor's global styles panel, which is exactly the kind of clean architecture the block editor was built for. For a deeper look at how block themes handle typography, the WordPress project on Wikipedia covers the history of FSE and how theme.json became the central configuration file.
Font Performance Tips
Limit Font Weights
Each weight is a separate file request. Load only what you display: typically 400 (regular) and 700 (bold). Skip 300, 500, and 600 unless your design genuinely uses them.
Use WOFF2 Only
WOFF2 support covers 97% of browsers as of 2026. Skip WOFF, TTF, and EOT fallbacks for new projects. Less files, faster load.
Preconnect for Google Fonts
If you use Google Fonts CDN, add a preconnect hint so the browser opens the connection early: <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Subset Your Font
If your site is English-only, use the Latin subset. Google Fonts adds &subset=latin automatically, but self-hosted fonts need manual subsetting with a tool like FontSquirrel's webfont generator.
Check your results in the WordPress performance guide after making font changes. Core Web Vitals in PageSpeed Insights will show whether your LCP and CLS scores improved.
Troubleshooting Common Issues
Font not showing up
Open browser DevTools, go to the Network tab, filter by "font", and reload the page. If the font file is not listed, the URL in your CSS or functions.php is wrong. Double-check the path, especially the theme folder name.
Font loads but CSS ignores it
A more specific CSS rule in the parent theme is overriding yours. Use DevTools to inspect the element and look for strikethrough font-family declarations. Increase specificity in your child theme CSS or add !important as a last resort.
Invisible text flash on load
Missing font-display: swap in your @font-face declaration. Add it and the browser will show the fallback font immediately instead of waiting for the custom font to download.
Block editor still shows old font
The editor loads its own stylesheet. If you registered fonts via theme.json, clear your browser cache and reload the editor. If you used functions.php, you may need to also enqueue the font on the admin side with add_action( 'enqueue_block_editor_assets', ... ).
Frequently Asked Questions
Do I need a plugin to add custom fonts to WordPress?
No. The three methods above cover everything a font plugin does, without adding a dependency to your site. Plugins help if you want a GUI for non-developers, but for performance and control, code-based methods win.
What is the best way to load Google Fonts in WordPress?
Self-hosting beats the CDN for performance. Download the WOFF2 files, upload to your theme, declare @font-face, and skip the external request entirely. If you prefer the CDN, enqueue via functions.php with a preconnect hint.
Will custom fonts slow down my site?
Only if you load them carelessly. Stick to WOFF2, limit to 2-3 weights, add font-display: swap, and use the Latin subset for English sites. Done right, the performance hit is negligible.
Does theme.json replace functions.php for adding fonts?
For block themes, yes. theme.json is cleaner and exposes fonts in the global styles panel. Classic themes still need functions.php. Pick the method that matches your theme type.
Sources & Further Reading
Custom fonts are one of the easiest ways to give your WordPress site a distinct visual identity, and you do not need a single plugin to do it. Pick the method that matches your theme type: functions.php for classic themes, @font-face for self-hosted performance, and theme.json for block themes. Then test with DevTools and PageSpeed Insights to confirm everything loads cleanly.
If you are still building out your theme setup, the guide to switching WordPress themes safely is a good next step before committing to font choices across your whole site.