---
title: Eliminate font loading delays with system fonts
description: >-
  Configure your theme to use system fonts to eliminate web font downloads
  entirely, removing a resource that would otherwise block text rendering.
source_url:
  html: >-
    https://shopify.dev/docs/storefronts/themes/best-practices/performance/use-system-fonts
  md: >-
    https://shopify.dev/docs/storefronts/themes/best-practices/performance/use-system-fonts.md
api_name: liquid
---

# Eliminate font loading delays with system fonts

Use system fonts to avoid downloading web font files before text can render.

***

## Why

Every web font file your theme loads creates a render dependency. Before styled text appears, the browser must:

1. Parse the HTML and discover the `@font-face` declaration.
2. Request the font file from the CDN.
3. Wait for the file to download.
4. Apply the font to text in the layout.

On a fast connection this takes tens of milliseconds. On a slow connection or mobile device, it can take several hundred milliseconds. During this time, browsers either show invisible text, known as FOIT (Flash of Invisible Text), or fall back to a system font before swapping after the web font arrives, known as FOUT (Flash of Unstyled Text). The swap itself can contribute to CLS if the web font's metrics differ from the fallback.

System fonts are already installed on the device. Using them means the browser can render text immediately: no network request, no swap, no layout shift from font loading.

### When to choose system fonts

* Performance is the top priority.
* The brand doesn't have a distinctive typographic identity that requires a custom font.
* The theme targets mobile users on constrained connections.
* You want to eliminate all font-related CLS.

### When web fonts are appropriate

* The brand requires a specific typeface for identity or design consistency.
* The performance cost has been measured and is acceptable for the target audience.
* You're already applying self-hosting and fallback font tuning. See [Self-host web fonts](https://shopify.dev/docs/storefronts/themes/best-practices/performance/self-host-web-fonts) and [Reduce CLS from font swapping](https://shopify.dev/docs/storefronts/themes/best-practices/performance/reduce-cls-font-swapping).

***

## How

### Use the system font stack in CSS

The system font stack selects the native UI font for each operating system:

```css
body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}
```

This renders in San Francisco on Apple devices, Segoe UI on Windows, and the platform default sans-serif on Linux and Android, all without a network request.

### Configure system fonts in theme settings

The Shopify [font picker setting](https://shopify.dev/docs/storefronts/themes/architecture/settings/input-settings#font_picker) lets merchants choose fonts. The `default` value is required, and it must be a real font handle: `system` isn't one. System fonts have their own handles, such as `system_ui_n4`, `sans_serif_n4`, `serif`, and `mono`.

A handle identifies one variant rather than a family, so the suffix only appears when the family has more than one variant. `system_ui` and `sans_serif` each ship four variants, named with a style letter and a weight digit: `n4` (normal 400), `i4` (italic 400), `n7` (normal 700), and `i7` (italic 700). `serif` and `mono` ship a single regular variant, so their handles have no suffix.

To default to the system UI font, use `system_ui_n4` in your theme's `settings_schema.json`:

## config/settings\_schema.json

```json
{
  "name": "Typography",
  "settings": [
    {
      "type": "font_picker",
      "id": "type_body_font",
      "label": "Body font",
      "default": "system_ui_n4"
    },
    {
      "type": "font_picker",
      "id": "type_header_font",
      "label": "Heading font",
      "default": "system_ui_n4"
    }
  ]
}
```

When the merchant's selected font is the system font, output the system font stack instead of a web font:

## layout/theme.liquid

```liquid
{%- assign body_font = settings.type_body_font -%}


{% style %}
  {{ body_font | font_face: font_display: 'swap' }}


  :root {
    {%- if body_font.system? -%}
      --font-body-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
        Roboto, "Helvetica Neue", Arial, sans-serif;
    {%- else -%}
      --font-body-family: {{ body_font.family }}, {{ body_font.fallback_families }};
    {%- endif -%}
  }
{% endstyle %}
```

The `font_face` filter outputs a CSS `@font-face` declaration, not an HTML tag, so it has to sit inside a stylesheet. The [`style` tag](https://shopify.dev/docs/api/liquid/tags/style) wraps its contents in a `<style>` element for you. Emitting `font_face` outside a stylesheet prints the declaration into the document as text and the font never registers.

When the selected font is a system font, `font_face` returns an empty string, because system fonts have no files on the CDN. That's why the call doesn't need a `system?` guard.

Then use the CSS variable throughout your theme:

```css
body {
  font-family: var(--font-body-family);
}
```

### Skip font preloads for system fonts

When `settings.type_body_font.system?` is `true`, don't preload a font file. `font_url` returns an empty string for system fonts, so an unguarded preload emits a tag with an empty `href`. Guard every preload and `preconnect` with a `system?` check, as shown below.

***

## Examples

### Checking whether the selected font is a system font

Use the `.system?` property on the font object to branch your template logic:

```liquid
{%- assign header_font = settings.type_header_font -%}


{%- unless header_font.system? -%}
  {%- comment -%} preload_tag outputs an HTML tag, so it belongs outside the stylesheet {%- endcomment -%}
  {{ header_font | font_url | preload_tag: as: 'font', type: 'font/woff2' }}
{%- endunless -%}


{% style %}
  {{ header_font | font_face: font_display: 'swap' }}


  :root {
    {%- if header_font.system? -%}
      --font-heading-family: system-ui, -apple-system, sans-serif;
    {%- else -%}
      --font-heading-family: {{ header_font.family }}, {{ header_font.fallback_families }};
    {%- endif -%}
  }
{% endstyle %}
```

`font_url` takes an optional positional format argument, `woff2` (the default) or `woff`. It has no `variant` parameter: to preload a different weight or style, pipe the font through [`font_modify`](https://shopify.dev/docs/api/liquid/filters/font_modify) first. Don't pass `crossorigin` to `preload_tag`, because it adds `crossorigin="anonymous"` on its own when `as` is `font`.

### Dawn theme pattern

Dawn implements this pattern in its `theme.liquid` head. It calls `font_face` unconditionally inside a `{% style %}` block, because the filter returns nothing for system fonts, and it guards the things that would otherwise emit a dead request: the `preconnect` to `fonts.shopifycdn.com` and each `preload` are wrapped in `{%- unless ... .system? -%}`. When the merchant selects a system font, no font requests are made at all.

***

## Testing

* **[Chrome DevTools Network panel](https://developer.chrome.com/docs/devtools/network)**: filter by **Font**. With system fonts configured, the waterfall should show no font requests at all: no requests to `fonts.shopifycdn.com`, no `/cdn/fonts/` requests on your store domain, and no font files from your theme's `assets/` directory.
* **CLS measurement**: use the **Performance** panel's **Layout Shifts** track to confirm that no layout shift occurs during or after initial render when system fonts are active.
* **FCP comparison**: compare FCP with and without system fonts using **Performance** > **Insights**. System fonts improve FCP by eliminating the font download from the critical path.
* **Visual review**: inspect the rendered page across operating systems, such as macOS, Windows, and Android, to confirm that the system font stack provides acceptable typographic quality.

***

## References

* [System fonts in Shopify theme settings](https://shopify.dev/docs/storefronts/themes/architecture/settings/fonts#system-fonts)
* [`font_picker` setting](https://shopify.dev/docs/storefronts/themes/architecture/settings/input-settings#font_picker)
* [`font_face`](https://shopify.dev/docs/api/liquid/filters/font_face) filter
* [`font_url`](https://shopify.dev/docs/api/liquid/filters/font_url) filter
* [`style`](https://shopify.dev/docs/api/liquid/tags/style) tag
* [Self-host web fonts on Shopify CDN](https://shopify.dev/docs/storefronts/themes/best-practices/performance/self-host-web-fonts)
* [Reduce CLS from font swapping](https://shopify.dev/docs/storefronts/themes/best-practices/performance/reduce-cls-font-swapping)
* [Use `preload` resource hints sparingly](https://shopify.dev/docs/storefronts/themes/best-practices/performance/use-preload-resource-hints-sparingly)

***
