Web Fonts: From Google to Self-Hosting Like a Pro
Web Fonts: From Google to Self-Hosting Like a Pro
If we want to use a font that doesn't come pre-installed on the user's device, we can download and use a custom font!
There are lots of ways to do it. In this lesson, we'll look at a couple of popular services that can help, and also see how to do it from scratch.
Using Google Fonts
Google Fonts is an online repository of free, open-source web fonts. They have hundreds of popular options. It also effectively works as a CDN for fonts; they serve the fonts for us, from their own servers.
Google Fonts works by providing a snippet that looks like this:
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@1,400;1,600&display=swap"
rel="stylesheet"
/>
Drop this in the <head>
of your HTML file.
Then use it in your CSS:
.thing {
font-family: "Open Sans", sans-serif;
}
Web fonts should be wrapped in quotes ('Open Sans', not Open Sans). It isn't strictly required for single-word fonts, but it's good practice.
No HTML file?
If you use a framework like Next.js, you may not have a traditional index.html
file.
Frameworks generate this file automatically, but they provide escape hatches. In Next.js, you can use the <Head />
component:
import Head from "next/head";
<Head>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap"
rel="stylesheet"
/>
</Head>;
The Benefits and Downsides of Google Fonts
Google Fonts is convenient, but not always the best for performance or variety.
- Lots of amazing fonts aren't on Google Fonts
- Self-hosted web fonts can perform better
Gatsby creator Kyle Matthews discovered that self-hosting fonts can save 300ms on desktop and 1s+ on mobile 3G.
Performance Trade-Offs
The issue isn't Google's server speed — it's about the loading process:
- Browser fetches and parses
index.html
- It sees the
<link>
to Google Fonts CSS, fetches it - Parses that CSS, sees font reference to
fonts.gstatic.com
, fetches it too
Because fonts.googleapis.com and fonts.gstatic.com are external domains, additional handshakes (like HTTPS) introduce overhead.
The font CSS request is blocking — the browser waits before rendering the page.
Self-hosting lets you skip Step 2 and 3 entirely, resulting in better performance.
In the past, fonts from Google Fonts might've been cached due to global CDN sharing. However, modern browsers now use partitioned caches, so every site must re-download fonts.
Using Modern Tooling: Fontsource
Fontsource by Vercel makes it easy to self-host fonts. You can install fonts using npm:
npm install @fontsource/open-sans
Then import it directly:
import "@fontsource/open-sans";
Fontsource supports all Google Fonts and other open-source fonts, ideal for modern JS frameworks.
Angular 11+ also supports Google Fonts with built-in configuration to inline fonts directly.
The Manual Way
If the font isn't on Google Fonts or Fontsource, self-host manually.
Converting formats
Most system fonts are .otf
or .ttf
— large and not optimized for web. Convert them to .woff2
using Font Squirrel's Webfont Generator.
Using @font-face
@font-face {
font-family: "Wotfard";
src: url("/fonts/wotfard-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}
Repeat for each weight and style:
@font-face {
font-family: "Wotfard";
src: url("/fonts/wotfard-medium.woff2") format("woff2");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "Wotfard";
src: url("/fonts/wotfard-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "Wotfard";
src: url("/fonts/wotfard-regular-italic.woff2") format("woff2");
font-weight: 400;
font-style: italic;
}
Each variation (italic, weight) requires its own declaration.
IE Support
Modern browsers all support .woff2
. IE doesn't.
@font-face {
font-family: "Wotfard";
src: url("/fonts/wotfard-regular-italic.woff2") format("woff2"), url("/fonts/wotfard-regular-italic.woff")
format("woff");
font-weight: 400;
font-style: normal;
}
Put .woff2
first so modern browsers use the smaller file.
Where to Place the Fonts
Add @font-face
declarations inside <style>
in your index.html
or in your main CSS file.
<style>
@font-face {
font-family: "Wotfard";
src: url("/fonts/wotfard-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}
</style>
Use like this:
.something {
font-family: "Wotfard";
font-weight: 400;
}
Static Files in React
React requires assets like fonts/images to go in the public/
folder (CRA) or static handling paths for frameworks like Next.js and Gatsby.
Faux Bolds and Italics
Without the real bold or italic font, browsers will simulate styles. These look worse than true variants.
True Bold – Clean and designed by the font creator.
Faux Bold – Lines artificially thickened by the browser.
Same goes for italic: browsers slant the font instead of using alternate characters.
Always provide actual bold/italic font files. Faux styles reduce visual quality.
Number Rounding
Suppose you load 400 and 900 weights. What happens when you write:
.thing {
font-weight: bold;
}
.thing {
font-weight: 700;
}
In the past, this would result in faux-bold. But modern browsers round to the closest available weight.
The font will round to 900, showing heavy text for both weights.
Using | Using |
---|---|
This text is bold. | This text is bold. |
Use font-weight: 700
instead of bold
for better control and precision.
Final Thoughts
- Google Fonts is simple, but may be slower
- Self-hosted fonts are faster and more customizable
- Fontsource makes self-hosting even easier
- Always use proper formats (
.woff2
,.woff
) - Avoid faux styles — real bold/italic files make a difference