2026年11月13日
2026年11月13日
Load Custom Fonts in WordPress: theme.json, @font-face, Self-Hosting
This article is a localized edition of the Japanese original. Read the Japanese original
How you add your own fonts to WordPress depends on how your theme is built.
| Method | Best for |
|---|---|
Register with fontFace in theme.json |
Block themes. Also works in classic themes that have a theme.json |
| Upload in the Font Library | Block themes, when you want to add fonts without code (WordPress 6.5 and later) |
Load @font-face CSS with wp_enqueue_style() |
Classic themes that do not use theme.json |
| Load the Google Fonts CSS | Quick setup (visitors' browsers connect to Google's servers) |
The behavior of the WordPress functions and hooks in this article was checked against the WordPress core source code (6.9).
Prepare the font files
If you serve fonts from your own server (self-hosting), prepare the files in WOFF2 format. WOFF2 is a compressed web font format supported by all current major browsers.
- Most fonts on Google Fonts use open licenses such as the SIL Open Font License, so you can host and serve them yourself. Check each font's license before using it.
- Commercial fonts and fonts installed on your computer are not necessarily licensed for web use. Check the terms of the license you bought.
- Tools such as google-webfonts-helper (gwfh.mranftl.com) let you download Google Fonts as WOFF2 files together with the CSS.
This article places the files in the theme like this. The file names are examples.
my-theme/
├── assets/
│ ├── css/fonts.css
│ └── fonts/
│ ├── noto-sans-jp-400.woff2
│ ├── noto-sans-jp-700.woff2
│ └── inter-variable.woff2
├── functions.php
├── style.css
└── theme.json
Japanese fonts contain many more characters, so their files are larger than Latin-only fonts. Limiting yourself to about two weights reduces how much has to load.
Method 1: Register fonts with fontFace in theme.json
In a block theme (or a classic theme with a theme.json), register fonts in settings.typography.fontFamilies of theme.json. When you add fontFace, WordPress outputs the @font-face CSS for you, both on the front end and in the block editor, so the editor needs no extra setup.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"typography": {
"fontFamilies": [
{
"name": "Noto Sans JP",
"slug": "noto-sans-jp",
"fontFamily": "\"Noto Sans JP\", sans-serif",
"fontFace": [
{
"fontFamily": "Noto Sans JP",
"fontWeight": "400",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": [ "file:./assets/fonts/noto-sans-jp-400.woff2" ]
},
{
"fontFamily": "Noto Sans JP",
"fontWeight": "700",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": [ "file:./assets/fonts/noto-sans-jp-700.woff2" ]
}
]
}
]
}
},
"styles": {
"typography": {
"fontFamily": "var(--wp--preset--font-family--noto-sans-jp)"
}
}
}
file:./insrcis a path relative to the folder that containstheme.json(the theme folder).- The
slugcreates the CSS variable--wp--preset--font-family--noto-sans-jp, and the font becomes selectable instylesand in the blocks' typography settings. - For a variable font (one file that covers a continuous range of weights), give a range such as
"fontWeight": "100 900".
If you already have a theme.json, add this font as another entry in the fontFamilies array. After saving, open the site with your browser's developer tools and check in the Network tab that the .woff2 files load.
Method 2: Upload in the Font Library (WordPress 6.5 and later)
WordPress 6.5 added the Font Library to the Site Editor. In a block theme, you can upload font files or install fonts from Google Fonts from the Typography screen in the Site Editor's Styles settings.
- Font files added through the Font Library are stored in
wp-content/uploads/fonts/by default and served from your own server. - They are saved as site data rather than theme files, so they remain when you switch themes.
Method 3: Load @font-face CSS (classic themes)
In a classic theme without theme.json, write the @font-face rules in a CSS file and load it with wp_enqueue_style().
/* assets/css/fonts.css */
@font-face {
font-family: "Noto Sans JP";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/noto-sans-jp-400.woff2") format("woff2");
}
@font-face {
font-family: "Noto Sans JP";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("../fonts/noto-sans-jp-700.woff2") format("woff2");
}
/* Variable font: specify the weight as a range */
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("../fonts/inter-variable.woff2") format("woff2");
}
/* Latin text in Inter, Japanese text in Noto Sans JP */
body {
font-family: "Inter", "Noto Sans JP", sans-serif;
}
Paths in url() are relative to the CSS file. With a stack like the final font-family, the browser looks for each character in the fonts from first to last, so characters that Inter lacks, such as Japanese, are shown in Noto Sans JP.
<?php
// functions.php
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_style(
'mytheme-fonts',
get_theme_file_uri( 'assets/css/fonts.css' ),
array(),
wp_get_theme()->get( 'Version' )
);
wp_enqueue_style(
'mytheme-style',
get_stylesheet_uri(),
array( 'mytheme-fonts' ), // load after fonts.css
wp_get_theme()->get( 'Version' )
);
} );
// Load the same fonts in the block editor
add_action( 'after_setup_theme', function () {
add_theme_support( 'editor-styles' );
add_editor_style( 'assets/css/fonts.css' );
} );
In a classic theme, CSS added with add_editor_style() is not loaded into the block editor unless you also call add_theme_support( 'editor-styles' ). Relative paths inside that CSS (../fonts/…) are resolved by WordPress against the theme URL.
Loading the Google Fonts CSS
You can also load the URL from the <link> tag that the Google Fonts site gives you with wp_enqueue_style().
<?php
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_style(
'mytheme-google-fonts',
'https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap',
array(),
null // do not add ?ver= to the Google URL
);
} );
- The Google Fonts CSS splits Japanese fonts into small files by character range, and only the files for the ranges used on the page are downloaded. Compared with self-hosting a Japanese font as one file, this can mean less data per page.
- On the other hand, visitors' browsers connect to Google's servers, so their IP addresses are sent to Google. If you need to be careful with personal data, for example on a site with many visitors from the EU, consider self-hosting.
Ways to make Google Fonts load faster are covered in How to optimize Google Fonts loading in WordPress.
font-display and preload
font-display controls how text is shown until the font has loaded.
| Value | Behavior |
|---|---|
swap |
Shows text right away in a fallback font and swaps when the font loads |
block |
Hides text for a short time while waiting |
optional |
If the font is not available almost immediately, keeps the fallback font for that page |
With swap the text is readable right away, but character widths change when the font swaps in, which can shift the layout. If the shift bothers you, consider optional.
Fonts used by text visible when the page first appears can be fetched early with preload. The wp_preload_resources filter, added in WordPress 6.1, outputs <link rel="preload"> in the <head>.
<?php
add_filter( 'wp_preload_resources', function ( $resources ) {
$resources[] = array(
'href' => get_theme_file_uri( 'assets/fonts/noto-sans-jp-400.woff2' ),
'as' => 'font',
'type' => 'font/woff2',
'crossorigin' => 'anonymous', // required for font preloads
);
return $resources;
} );
- Font preloads need the
crossoriginattribute even for files on your own server. Without it, the preloaded file is not used and the same font is downloaded twice. - Preload only the fonts actually used on the first screen. Preloading fonts you do not use delays other downloads.
If the font does not appear
- Check in the Network tab of your browser's developer tools whether the
.woff2files return 404. Wrong paths inurl()orfile:./are a common cause. - If you serve font files from another domain (such as a CDN), that server must send an
Access-Control-Allow-Originheader. - If you use a caching plugin or a CDN, clear the cache before checking again.
Other causes and how to check them are covered in How to fix web fonts not loading in WordPress.
Summary
- In block themes, fonts registered with
fontFaceintheme.jsonload on both the front end and in the editor - In classic themes, load the
@font-faceCSS withwp_enqueue_style(), and in the editor withadd_editor_style() - When self-hosting, check the font license and keep Japanese fonts to a few weights
font-display: swapshows text sooner but can shift the layout, and preloads needcrossorigin
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)