Skip to content

Commit da8e0e2

Browse files
authored
Merge pull request #10 from codeAdrian/feature/sync-mode
Render-blocking mode & preload array
2 parents ffc1692 + 7ce48ee commit da8e0e2

20 files changed

+159
-102
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.gitignore

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2020 Adrian Bece
3+
Copyright (c) 2021 Adrian Bece
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+46-25
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,19 @@
22
<img src="https://res.cloudinary.com/dazdt97d3/image/upload/c_scale,q_auto:best,w_200/v1606558223/omni-logo.jpg" alt="Omni font loader logo">
33
<br/><br/>
44
<h1>Gatsby Omni Font Loader</h1>
5+
</div>
56

6-
Performant asynchronous font loading & Flash Of Unstyled Text (FOUT) handling plugin for Gatsby.
7+
* Simple way to add webfonts or custom fonts to Gatsby project
8+
* Performant asynchronous font loading can be enabled
9+
* Font loading listener can be enabled
10+
* Flash Of Unstyled Text (FOUT) handling support
711

12+
<div align="center">
813
<br/>
9-
<img src="https://badgen.net/github/tag/codeAdrian/gatsby-omni-font-loader" />
10-
&nbsp;
11-
<img src="https://badgen.net/npm/dt/gatsby-omni-font-loader" />
12-
&nbsp;
13-
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" />
14+
<img src="https://badgen.net/github/tag/codeAdrian/gatsby-omni-font-loader" /> <img src="https://badgen.net/npm/dt/gatsby-omni-font-loader" /> <img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" />
1415
<br/><br/>
1516

16-
<img src="https://badgen.net/github/stars/codeAdrian/gatsby-omni-font-loader" />
17-
&nbsp;
18-
<img src="https://badgen.net/github/open-issues/codeAdrian/gatsby-omni-font-loader" />
19-
&nbsp;
20-
<img src="https://badgen.net/github/closed-issues/codeAdrian/gatsby-omni-font-loader" />
21-
&nbsp;
22-
<img src="https://badgen.net/github/last-commit/codeAdrian/gatsby-omni-font-loader/main" />
23-
&nbsp;
24-
<img src="https://badgen.net/github/license/codeAdrian/gatsby-omni-font-loader" />
25-
&nbsp;
26-
<img src="https://badgen.net/packagephobia/install/gatsby-omni-font-loader" />
17+
<img src="https://badgen.net/github/stars/codeAdrian/gatsby-omni-font-loader" /> <img src="https://badgen.net/github/open-issues/codeAdrian/gatsby-omni-font-loader" /> <img src="https://badgen.net/github/closed-issues/codeAdrian/gatsby-omni-font-loader" /> <img src="https://badgen.net/github/last-commit/codeAdrian/gatsby-omni-font-loader/main" /> <img src="https://badgen.net/github/license/codeAdrian/gatsby-omni-font-loader" /> <img src="https://badgen.net/packagephobia/install/gatsby-omni-font-loader" />
2718
</div>
2819
<br/><br/>
2920

@@ -56,6 +47,9 @@ Add the following snippet to `gatsby-config.js` plugins array.
5647
/* Plugin options */
5748
options: {
5849

50+
/* Font loading mode */
51+
mode: "async",
52+
5953
/* Enable font loading listener to handle FOUT */
6054
enableListener: true,
6155

@@ -101,24 +95,24 @@ Add the following snippet to `gatsby-config.js` plugins array.
10195
</tr>
10296
</thead>
10397
<tbody>
98+
<tr>
99+
</tr>
100+
<td>mode</td>
101+
<td>Can be set to `"async"` (default) or `"render-blocking"`. In `async` mode, fonts are loaded in optimal way, but FOUT is visible. In `render-blocking` mode FOUT will happen in rare cases, but the font files will become render-blocking.</td>
102+
<td>async</td>
104103
<tr>
105104
<td>enableListener</td>
106-
<td>Enable font loading listener to handle Flash Of Unstyled Text. If enabled, CSS classes will be applied to HTML once each font has finished loading.</td>
105+
<td>Works in `async` mode. Enable font loading listener to handle Flash Of Unstyled Text. If enabled, CSS classes will be applied to HTML once each font has finished loading.</td>
107106
<td>false</td>
108107
</tr>
109-
<tr>
110-
<td>preconnect</td>
111-
<td>URLs used for preconnect meta. Base URL where <strong>font files</strong> are hosted.</td>
112-
<td>[]</td>
113-
</tr>
114108
<tr>
115109
<td>interval</td>
116-
<td>Font listener interval (in ms). Default is 300ms. Recommended: >=300ms. </td>
110+
<td>Works if `enableListener` is `true`. Font listener interval (in ms). Default is 300ms. Recommended: >=300ms. </td>
117111
<td>300</td>
118112
</tr>
119113
<tr>
120114
<td>timeout</td>
121-
<td>Font listener timeout value (in ms). Default is 30s (30000ms). Listener will no longer check for loaded fonts after timeout, fonts will still be loaded and displayed, but without handling FOUT.</td>
115+
<td>Works if `enableListener` is `true`. Font listener timeout value (in ms). Default is 30s (30000ms). Listener will no longer check for loaded fonts after timeout, fonts will still be loaded and displayed, but without handling FOUT.</td>
122116
<td>30000</td>
123117
</tr>
124118
<tr>
@@ -131,9 +125,36 @@ Add the following snippet to `gatsby-config.js` plugins array.
131125
<td>Web fonts config. File link should point to font CSS file. Array of <code>{name: "Font name", file: "https://url-to-font-css.path"}</code> objects.</td>
132126
<td>[]</td>
133127
</tr>
128+
<tr>
129+
<td>preconnect</td>
130+
<td>URLs used for preconnect meta. Base URL where <strong>font files</strong> are hosted.</td>
131+
<td>[]</td>
132+
</tr>
133+
<tr>
134+
<td>preload</td>
135+
<td>Additional URLs used for preload meta. Preload for URLs provided under `file` attribute of `custom` and `web` fonts are automatically generated.</td>
136+
<td>[]</td>
137+
</tr>
134138
<tbody>
135139
</table>
136140

141+
## Async mode vs Render-blocking mode
142+
### Async mode
143+
Load font stylesheets and files in low-priority mode. If you want to add fonts in a performant way, handle FOUT on your own and make sure that the page render times are low, you should use `async` mode.
144+
145+
__Pros:__ Performance, content is displayed before font files are downloaded and parsed
146+
<br/>
147+
__Cons:__ FOUT needs to be handled
148+
149+
### Render-blocking mode
150+
Load font stylesheets and files in high-priority mode. If you want to use this plugin as a simple way to add fonts to your project as you would do in any other project, without any performance optimizations and FOUT handling, you should use `render-blocking` mode.
151+
152+
__Pros:__ Simple markup, FOUT won't occur in most cases
153+
<br/>
154+
__Cons:__ Font stylesheets and font files can delay first content paint time
155+
156+
157+
137158
## Handling FOUT with Font loading listener
138159

139160
When loading fonts asynchronously, Flash Of Unstyled Text (FOUT) might happen because fonts load few moments later after page is displayed to the user.

components/FontListener.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ declare var document: { fonts: any }
66

77
interface Props {
88
fontNames: string[]
9-
interval?: number
10-
timeout?: number
9+
interval: number
10+
timeout: number
1111
}
1212

1313
export const FontListener: React.FC<Props> = ({
1414
fontNames,
15-
interval = 300,
16-
timeout = 30000,
15+
interval,
16+
timeout,
1717
}) => {
1818
const [hasLoaded, setHasLoaded] = useState<Boolean>(false)
1919
const [loadedFonts, setLoadedFonts] = useState<string[]>([])

consts/defaults.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const INTERVAL_DEFAULT = 300
2+
3+
export const TIMEOUT_DEFAULT = 30000
4+
5+
export const MODE_DEFAULT = "async"

consts/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./defaults"

gatsby-browser.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
import React from "react"
22
import { AsyncFonts, FontListener } from "./components"
3+
import { INTERVAL_DEFAULT, MODE_DEFAULT, TIMEOUT_DEFAULT } from "./consts"
34
import { getFontFiles, getFontNames } from "./utils"
45

56
export const wrapRootElement = (
67
{ element },
7-
{ custom = [], web = [], enableListener, interval, timeout }
8+
{
9+
custom = [],
10+
web = [],
11+
enableListener,
12+
interval = INTERVAL_DEFAULT,
13+
timeout = TIMEOUT_DEFAULT,
14+
mode = MODE_DEFAULT,
15+
}
816
) => {
17+
if (mode !== "async") {
18+
return element
19+
}
20+
921
const allFonts = [...custom, ...web]
1022
const fontFiles = getFontFiles(allFonts)
1123
const fontNames = getFontNames(allFonts)

gatsby-ssr.js

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
1-
import {
2-
getFontConfig,
3-
getFontFiles,
4-
getFontNames,
5-
getTestFonts,
6-
} from "./utils"
1+
import { MODE_DEFAULT } from "./consts"
2+
import { getFontConfig, getTestFonts } from "./generators"
3+
import { getFontFiles, getFontNames } from "./utils"
74

85
export const onRenderBody = (
96
{ setHeadComponents, setPostBodyComponents },
10-
{ enableListener, preconnect = [], web = [], custom = [] }
7+
{
8+
enableListener,
9+
preconnect = [],
10+
preload = [],
11+
web = [],
12+
custom = [],
13+
mode = MODE_DEFAULT,
14+
}
1115
) => {
1216
const allFonts = [...web, ...custom]
13-
const preload = getFontFiles(allFonts)
17+
const allPreloads = preload.concat(getFontFiles(allFonts))
1418
const fontNames = getFontNames(allFonts)
1519

16-
const preloadConfig = getFontConfig(preconnect, preload)
20+
const preloadConfig = getFontConfig(
21+
preconnect,
22+
allPreloads,
23+
mode === "async" ? [] : allFonts
24+
)
1725

18-
if (enableListener && Boolean(allFonts.length)) {
26+
if (enableListener && Boolean(allFonts.length) && mode === "async") {
1927
const testFontConfig = getTestFonts(fontNames)
2028
setPostBodyComponents(testFontConfig)
2129
}
2230

23-
if(preloadConfig && Boolean(preloadConfig.length)) {
31+
if (preloadConfig && Boolean(preloadConfig.length)) {
2432
setHeadComponents(preloadConfig)
2533
}
2634
}

generators/getFontConfig.tsx

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from "react"
2+
import { arrayCheck, kebabCase } from "../utils"
3+
4+
export const getFontConfig = (
5+
preconnectConfig: string[],
6+
preloadConfig: string[],
7+
renderBlockingFonts?: { name: string | string[]; file: string }[]
8+
) => {
9+
const headComponents = []
10+
11+
if (arrayCheck(preconnectConfig)) {
12+
preconnectConfig.forEach(href => {
13+
headComponents.push(
14+
<link
15+
key={`preconnect-${href}`}
16+
rel="preconnect"
17+
href={href}
18+
crossOrigin="true"
19+
/>
20+
)
21+
})
22+
}
23+
24+
if (arrayCheck(preloadConfig)) {
25+
preloadConfig.forEach(href => {
26+
headComponents.push(
27+
<link key={`preload-${href}`} rel="preload" as="style" href={href} />
28+
)
29+
})
30+
}
31+
32+
if (arrayCheck(renderBlockingFonts)) {
33+
renderBlockingFonts.forEach(({ name, file }) => {
34+
const key = Array.isArray(name)
35+
? name.map(n => kebabCase(n)).join("-")
36+
: kebabCase(name)
37+
headComponents.push(
38+
<link key={`render-blocking-${key}`} href={file} rel="stylesheet" />
39+
)
40+
})
41+
}
42+
43+
return headComponents
44+
}
File renamed without changes.

generators/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./getFontConfig"
2+
export * from "./getTestFonts"

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@
2828
},
2929
"peerDependencies": {
3030
"gatsby": ">=1",
31-
"react-helmet": "^6.1.0"
31+
"react-helmet": ">=6.0.0"
3232
}
3333
}

utils/arrayCheck.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const arrayCheck = (arr?: unknown) =>
2+
arr && Array.isArray(arr) && Boolean(arr.length)

utils/getFontConfig.tsx

-39
This file was deleted.

utils/getFontFiles.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const getFontFiles = (allFonts: { file: string }[]) =>
2+
allFonts.map(({ file }) => file)

utils/getFontNames.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const getFontNames = (allFonts: { name: string }[]) => {
2+
const fontNames = []
3+
allFonts.forEach(({ name }) =>
4+
Array.isArray(name) ? fontNames.push(...name) : fontNames.push(name)
5+
)
6+
return fontNames
7+
}

utils/index.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
export * from "./getFontConfig"
2-
export * from "./getTestFonts"
3-
export * from "./utils"
1+
export * from "./arrayCheck"
2+
export * from "./kebabCase"
3+
export * from "./getFontFiles"
4+
export * from "./getFontNames"

utils/kebabCase.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const kebabCase = (str: string) =>
2+
str
3+
.match(/[A-Z]{2,}(?=[A-Z][a-z0-9]*|\b)|[A-Z]?[a-z0-9]*|[A-Z]|[0-9]+/g)
4+
.filter(Boolean)
5+
.map(x => x.toLowerCase())
6+
.join("-")

utils/utils.ts

-17
This file was deleted.

0 commit comments

Comments
 (0)