react-fout-stager v3.0.0
React FOUT Stager
Load weights and styles of typefaces in stages. Based on the techniques of @zachleat's work on FOUT with Two Stage Render.

Features
- Load multiple typefaces with weights and styles in potentially multiple stages
- Caches/optimizes repeat views per session with sessionStorage
- Receive event when all fonts have been loaded
- Should work in IE9+
Installation
You can install react-fout-stager via Yarn or npm:
# If using Yarn:
yarn add react-fout-stager
# If using npm:
npm install --save react-fout-stagerThe core component from react-fout-stager is FoutStager.
This module can be required via ES imports, CommonJS require, or UMD.
import FoutStager from 'react-fout-stager';
// using require
const FoutStager = require('react-fout-stager');Important! If you need to use the ES5 version, such as within Create React App,
you will need to import the component at react-fout-stager/es5, e.g.:
import FoutStager from 'react-fout-stager/es5/FoutStager';
// using require
const FoutStager = require('react-fout-stager/es5/FoutStager');Usage
After importing react-fout-stager, it can be rendered anywhere in the React tree. Once
react-fout-stager will mount, it will kick off loading any typefaces defined in the stages prop.
import React from 'react';
import { render } from 'react-dom';
import FoutStager from 'react-fout-stager';
render((
<FoutStager stages={[{
className: 'font-stage-primary',
families: [{ family: 'Roboto' }],
stages: [{
className: 'font-stage-secondary',
families: [
{ family: 'RobotoBold', options: { weight: 700 } },
{ family: 'RobotoItalic', options: { style: 'italic' } },
{ family: 'RobotoBoldItalic', options: { weight: 700, style: 'italic' } }
]
}]
}]} />
), document.getElementById('root'));If you pass any children to FoutStager, they will not be rendered until the typefaces have all loaded.
import React from 'react';
import { render } from 'react-dom';
import FoutStager from 'react-fout-stager';
render((
<FoutStager stages={[{
className: 'font-stage-primary',
families: [{ family: 'Roboto' }],
stages: [{
className: 'font-stage-secondary',
families: [
{ family: 'RobotoBold', options: { weight: 700 } },
{ family: 'RobotoItalic', options: { style: 'italic' } },
{ family: 'RobotoBoldItalic', options: { weight: 700, style: 'italic' } }
]
}]
}]}>
<em>I won't render until all these "Roboto" typefaces have loaded!</em>
</FoutStager>
), document.getElementById('root'));Props
FoutStager accepts a few props to customize its behavior:
| Property | Type | Required? | Description |
|---|---|---|---|
stages | arrayOf(stage) | ✓ | A list of stages with associated typefaces to load. |
sessionKey | string | The sessionStorage key which is used to cache typeface loads. Defaults to foutStagerLoaded. This will need to be overridden if you use multiple FoutStagers in an application. | |
onStagesLoad | func | Execute a function when all typefaces have loaded. | |
children | node | Render children when all typefaces have loaded. |
stages
The stages prop of FoutStager accepts an array of stages. The shape of a stage is:
Stage :: {
className: string.isRequired,
families: arrayOf(shape({
family: string.isRequired,
options: object
})).isRequired,
stages: arrayOf(Stage)
}Or said more verbosely:
- Each stage is an object.
- A stage must include a property named
classNamewhich is astringclass name to place on thehtmltag when the stage has loaded. - A stage must include a property named
familieswhich is an array of objects, each of which represents a typeface (font family) to load in this stage. - Each family in
familiesmust include a property namedfamilywhich corresponds to afont-familydefined in CSS. Creating font families is detailed below. - Each family in
familiescan optionally include a property namedoptions, which is an object that defines a font description with properties ofweight,style, andstretch. These accept values that are defined by thefontfaceobserverlibrary. - A stage can also include a property named
stages, which is another array of stages to load once the current stage has completed.
All adjacent stages are loaded in parallel. The use of the stages property on a stage should be use
for loading stages serially.
CSS
In order to load typefaces with FoutStager, they need to have a defined font-family in CSS.
For example, here is a @font-face with a font-family defined for the Roboto (Regular) typeface:
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
local('Roboto Regular'),
local('Roboto Regular '),
local('Roboto-Regular'),
url('~typeface-roboto/files/roboto-latin-400.woff2') format('woff2'),
url('~typeface-roboto/files/roboto-latin-400.woff') format('woff');
}Take note of the font-family: Roboto property. This is how you will reference this particular
font in FoutStager:
<FoutStager stages={[{
className: 'font-stage-primary',
families: [{ family: 'Roboto' }]
}]} />Loading variants of a typeface should have their own font-family name. For example, to define
the Roboto Italic font:
@font-face {
font-family: RobotoItalic;
font-style: italic;
font-display: swap;
font-weight: 400;
src:
local('Roboto Regular italic'),
local('Roboto-Regularitalic'),
url('~typeface-roboto/files/roboto-latin-400italic.woff2') format('woff2'),
url('~typeface-roboto/files/roboto-latin-400italic.woff') format('woff');
}<FoutStager stages={[{
className: 'font-stage-primary',
families: [{ family: 'RobotoItalic', options: { style: 'italic' } }]
}]} />Notice the use of options when loading fonts with non-normal font descriptions.
Example
This demo can be seen in action on the hosted styleguide.
For a canonical example, we are going to replicate Zach Leatherman's FOFT, or FOUT with Two Stage Render demo in React.
@font-face {
font-family: Lato;
src:
url('/files/lato-regular-webfont.woff2') format('woff2'),
url('/files/lato-regular-webfont.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: LatoBold;
src:
url('/files/lato-bold-webfont.woff2') format('woff2'),
url('/files/lato-bold-webfont.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: LatoItalic;
src:
url('/files/lato-italic-webfont.woff2') format('woff2'),
url('/files/lato-italic-webfont.woff') format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: LatoBoldItalic;
src:
url('/files/lato-bolditalic-webfont.woff2') format('woff2'),
url('/files/lato-bolditalic-webfont.woff') format('woff');
font-weight: 700;
font-style: italic;
}
/*
The purpose of defining class stages is to
re-render once a stage has been met. We start
with the minimal default stage of sans-serif,
and progressively re-render.
*/
body {
font-family: sans-serif;
}
/*
The defined stages now modify the display of
elements once they are loaded.
*/
/*
During stage 1 we only load the Lato font.
Once it's loaded, update the body to use it.
*/
.fonts-stage-1 body {
font-family: Lato, sans-serif;
font-weight: 400;
font-style: normal;
}
/*
During stage 2 we load Lato Bold, Lato Italic, and
Lato Bold Italic. Once these 3 are loaded, we can
once again update relevant elements to render using
these fonts.
*/
.fonts-stage-2 h1, .fonts-stage-2 strong {
font-family: LatoBold, sans-serif;
font-weight: 700;
}
.fonts-stage-2 em {
font-family: LatoItalic, sans-serif;
font-style: italic;
}
.fonts-stage-2 strong em,
.fonts-stage-2 em strong {
font-family: LatoBoldItalic, sans-serif;
font-weight: 700;
font-style: italic;
}import React from 'react';
import { render } from 'react-dom';
import FoutStager from 'react-fout-stager';
import './lato-family.css';
render((
<div>
<h1>FOFT, or FOUT with Two Stage Render</h1>
<p>
This is a paragraph.
<strong> This is heavier text.</strong>
<em> This is emphasized text.</em>
<strong><em> This is heavier and emphasized text.</em></strong>
</p>
<FoutStager
stages={[{
className: 'fonts-stage-1',
families: [{ family: 'Lato' }],
stages: [{
className: 'fonts-stage-2',
families: [
{ family: 'LatoBold', options: { weight: 700 } },
{ family: 'LatoItalic', options: { style: 'italic' } },
{ family: 'LatoBoldItalic', options: { weight: 700, style: 'italic' } }
]
}]
}]} />
</div>
)), document.getElementById('root'));Development and Contributing
This repository uses Neutrino and the following Neutrino middleware for developing, previewing, and building React components:
To get started:
- Fork and clone this repo.
- Install the dependencies with
yarn. - Start the development server with
yarn start. - Use CTRL-C to exit the development server.
- Use
yarn buildto generate the compiled component for publishing to npm.
Feel free to open an issue, submit a pull request, or contribute however you would like. Understand that this documentation is always a work in progress, so file an issue or submit a PR to ask questions or make improvements. Thanks!