How to add Google fonts to WordPress themes

When enqueuing Google fonts, there are five things to consider:

  1. Is the font enqueued instead of included directly in the template files or CSS?
  2. Is the font enqueued on the correct hook?
  3. Is the font URL protocol independent?
  4. Can translators deactivate the font if their language’s character set isn’t supported?
  5. Can the font be dequeued by child themes?

In this post, we’ll go over the best practices for enqueuing Google fonts in your WordPress theme.

Introducing the Font URLs function

Instead of hardcoding URLs to Google font resources, we will write a function to take care of all the logic related to these fonts. Let’s start out with the basic outline of the function. It takes no parameters and returns a string, which starts out empty.

function theme_slug_fonts_url() {
$fonts_url = '';

return $fonts_url;
}

Now we’ll add two fonts: Lora and Open Sans.

/* Translators: If there are characters in your language that are not
* supported by Lora, translate this to 'off'. Do not translate
* into your own language.
*/
$lora = _x( 'on', 'Lora font: on or off', 'theme-slug' );

/* Translators: If there are characters in your language that are not
* supported by Open Sans, translate this to 'off'. Do not translate
* into your own language.
*/
$open_sans = _x( 'on', 'Open Sans font: on or off', 'theme-slug' );

We use the WordPress _x() translation function to make the text “on” available for translation, while adding context via the second parameter. We also added a PHP comment to give further instructions to the translators in code.

As we use a translation function, the text above will show up in translation tools like GlotPress. If translators want to enable the font, they translate “on” as “on”. If they want to disable it, they translate it to “off”.

This may seem weird, but it’s an effective way to allow international users to keep their website from breaking. With that done, we can construct the correct URL depending on what fonts are enabled.

if ( 'off' !== $lora || 'off' !== $open_sans ) {
$font_families = array();

if ( 'off' !== $lora ) {
$font_families[] = 'Lora:400,700,400italic';
}

if ( 'off' !== $open_sans ) {
$font_families[] = 'Open Sans:700italic,400,800,600';
}
}

First, we check to see if both fonts have been deactivated by the translators; no need to continue in that case. Then we add the font names and styles to an array. The numbers after the colon represent different font styles—weights, cursive variants, small caps, etc.

It’s important that you only load the font styles you need to ensure that the loading time is as short as possible.

Now that we have this data, we can generate the fonts URL. The following code should be placed into the if statement above.

$query_args = array(
'family' => urlencode( implode( '|', $font_families ) ),
'subset' => urlencode( 'latin,latin-ext' ),
);

This code combines the array of font families into a single string, separated with the | (pipe) symbol. It also adds the subset parameter that determines which character sets to use. As with the weights, only load the character sets you need.

The PHP function urlencode() ensures that the data we pass to it gets encoded correctly to be used as a query part in a URL. The final step is appending this data to the URL to Google’s font servers.

$fonts_url = add_query_arg( $query_args, 'https://fonts.googleapis.com/css' );

Here we use the add_query_arg() function to build the URL. The complete function looks like this:

function theme_slug_fonts_url() {
$fonts_url = '';

/* Translators: If there are characters in your language that are not
* supported by Lora, translate this to 'off'. Do not translate
* into your own language.
*/
$lora = _x( 'on', 'Lora font: on or off', 'theme-slug' );

/* Translators: If there are characters in your language that are not
* supported by Open Sans, translate this to 'off'. Do not translate
* into your own language.
*/
$open_sans = _x( 'on', 'Open Sans font: on or off', 'theme-slug' );

if ( 'off' !== $lora || 'off' !== $open_sans ) {
$font_families = array();

if ( 'off' !== $lora ) {
$font_families[] = 'Lora:400,700,400italic';
}

if ( 'off' !== $open_sans ) {
$font_families[] = 'Open Sans:700italic,400,800,600';
}

$query_args = array(
'family' => urlencode( implode( '|', $font_families ) ),
'subset' => urlencode( 'latin,latin-ext' ),
);

$fonts_url = add_query_arg( $query_args, 'https://fonts.googleapis.com/css' );
}

return esc_url_raw( $fonts_url );
}

And this is the encoded URL string that is returned:

https://fonts.googleapis.com/css?family=Lora%3A400%2C700%2C400italic%7COpen+Sans%3A700italic%2C400%2C800%2C600&subset=latin%2Clatin-ext

With this function complete, we can enqueue our fonts.

Enqueuing on the front end

Enqueuing the font on the front end for your visitors is straightforward:

function theme_slug_scripts_styles() {
wp_enqueue_style( 'theme-slug-fonts', theme_slug_fonts_url(), array(), null );
}
add_action( 'wp_enqueue_scripts', 'theme_slug_scripts_styles' );

Our fonts are included with a unique handle prefixed with the theme slug, with no dependencies declared and no version number indicated. This allows the fonts to be dequeued by a child theme, either using the handle with wp_dequeue_style(), or by removing the entire function from the hook with remove_action(). Note the wp_enqueue_scripts action, which is the correct hook for adding stylesheets and scripts to your theme.

Adding Google fonts to the editor

If your theme uses editor styles, you need to enqueue the fonts on the back end as well. You can do this using the add_editor_style() function. It accepts an array of stylesheets to enqueue, and you can pass your font URL function as one of them:

function theme_slug_editor_styles() {
add_editor_style( array( 'editor-style.css', theme_slug_fonts_url() ) );
}
add_action( 'after_setup_theme', 'theme_slug_editor_styles' );

Note that the correct hook is after_setup_theme this time.

Adding fonts to the Custom Header screen

The legacy way to customize a theme’s header image is done via the Appearance → Header screen. To ensure the preview is displayed correctly in the admin, you need to include the Google fonts like so:

function theme_slug_custom_header_fonts() {
wp_enqueue_style( 'theme-slug-fonts', theme_slug_fonts_url(), array(), null );
}
add_action( 'admin_print_styles-appearance_page_custom-header', 'theme_slug_custom_header_fonts' );

Using the admin_print_styles-appearance_page_custom-header action, we can ensure that the fonts only get loaded on this particular screen and not on every admin page load.

That’s it!

If you have any questions, let us know!

5 responses

  1. Excellent summary!

    What if you add one new fonts in child theme and/or dequeue parent themes fonts. How would you add fonts in child theme so that they would work in editor and in header screen? Would the process look pretty much the same?

    1. Fränk Klein Avatar
      Fränk Klein

      Fonts can be dequeued using the unique handle used during the enqueuing. If you want to add new fonts in a child theme, the process would be very much the same.

      You could also add a function_exists() call around your fonts URL function, which would make it possible to override it in a child theme. This would make switching out fonts a lot easier in child themes.

  2. thanks for the article. i’ve been enjoying learning wordpress and php using the _s theme, and i really appreciate themeshaper.com as well as wptavern.com where the articles are less intimidating while still being quite technical. many things are still way over my head, but i find it all to be a good learning experience.
    as far as the above article, i did have some questions aside from not being able to keep the edited code i used for my site from breaking it.
    1) for little, learner-guys like myself, i’d imagine that knowing my site will never need any kind of translation precludes it from adding the depth of functionality the article and function provides. i like learning for the sake of it and don’t have a problem knowing this exists and should (probably) be used on a site like amazon.com, for example. but for myself, probably not, right?
    2) on the other hand, what do i know about it? not much, indeed. so perhaps a follow up article on when this kind of function should be used (or not) might be something others could benefit from.
    3) what is a theme_slug, please? i’ve never heard or seen this term used before (again, no doubt because i’m such a new-b) and found it confusing. i found no such term in the new wp online reference, but also found that the term ‘slug’ refers to posts and pages. i assumed it was the theme name you were referring to, but since i couldn’t get the code to stop breaking my site i’m still unclear.
    3a) should ‘theme_slug’ conform to specific standards, please? is using periods and dashes incorrect or otherwise problematic? should theme_slugs be one word only? where can i learn more about this ‘theme_slug’?
    3b) for the _x() function, could you please explain why you use ‘theme_slug’ and not $domain, as the reference states.
    thank you! (and sorry for the long post / questions and my obvious confusion).

    1. 1) For a custom theme for your own site you can do pretty much anything you want and ignore this article. This article is more about public released themes which you see in WordPress.org or WordPress.com. In public released themes you should follow all the possible standards and this one is a good tip for Google fonts.

      3) If your theme name is Super Duper then theme folder name should be super-duper. So theme-slug in this case is super-duper. Also every function should be prefixed super_duper.

  3. Fränk Klein Avatar
    Fränk Klein

    Thanks for your kind feedback.

    In my opinion, you can never know enough nor learn enough. So if you like learning new things, then don’t limit yourself to only what you need right now.

    Internationalization is such a huge part of WordPress that it would be a pity to consider it a “maybe later” item.

    Most of your questions are covered in the chapter about Internationalization in the Theme Developer Handbook: http://make.wordpress.org/docs/theme-developer-handbook/part-two-theme-functionality/internationalization/ Reading through that will give you a much more precise idea about how internationalization is handled in WordPress.

    For the specific question about the _x() function, the $domain indicated in the reference is just a placeholder for the real content. What it means in this case is that this is a function parameter and that it is of the type string, so text.