Skip to content

Commit 6edfc22

Browse files
chore(examples): Add loadable components (#31954)
Co-authored-by: LekoArts <[email protected]>
1 parent b5f93e9 commit 6edfc22

File tree

11 files changed

+982
-0
lines changed

11 files changed

+982
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Gatsby + loadable-components
2+
3+
This repo is meant to illustrate the pattern of adding [loadable-components](https://loadable-components.com/docs/getting-started/) to a Gatsby project. In this case, the Header component is being code-split out while maintaining SSR.
4+
5+
```jsx
6+
// src/components/layout.jsx
7+
import * as React from "react"
8+
9+
const Header = loadable(() => import("./header"))
10+
export default Header
11+
```
12+
13+
## Why?
14+
15+
Why do you need to do this when Gatsby already has excellent out-of-the-box code-splitting configuration?
16+
17+
There is one main scenario: _conditional rendering_. This can come in two forms:
18+
19+
- flex layouts from CMS content
20+
- logic in a component that may or may not render an import
21+
22+
These are essentially the same thing, but at different scales.
23+
24+
### Flex Layouts
25+
26+
When you build a page with a headless CMS, parts of your site may have a structured layout that is defined in code by components and can only be changed with a code change. Other pages, due to business cases or user preferences, need to have flexible layouts where a content editor can mix and reorder components in the CMS, and have that layout reflected in the code.
27+
28+
The most common pattern is a switch case or series of if statements checking the component’s type, rendering the correct one and passing in its props.
29+
30+
```jsx
31+
// src/components/flex-page.jsx
32+
import * as React from "react"
33+
34+
const FlexPage = ({ components }) => {
35+
return (
36+
<>
37+
{components.map(component => {
38+
switch (component.type) {
39+
case "header":
40+
return <Header {...component} />
41+
case "text":
42+
return <Text {...component} />
43+
case "carousel":
44+
return <Carousel {...component} />
45+
default:
46+
return <div>empty</div>
47+
}
48+
})}
49+
</>
50+
)
51+
}
52+
53+
export default FlexPage
54+
```
55+
56+
### Logical Checks in Render
57+
58+
This is basically the above pattern but on a smaller scale.
59+
60+
```jsx
61+
import * as React from "react"
62+
63+
const FlexPage = ({ hasCarousel }) => {
64+
return (
65+
<div>
66+
<Heading />
67+
<Text />
68+
{hasCarousel && <Carousel />}
69+
</div>
70+
)
71+
}
72+
73+
export default FlexPage
74+
```
75+
76+
### The Conditional Drawback
77+
78+
So what’s the problem with the patterns above? A content editor can build a page with a Heading, Text or a Carousel. Technically, the page will display as expected, but imagine a page where the editor only choose a Heading and Text. In that case, the Carousel and its dependencies will still be on the page!
79+
80+
This is because Gatsby’s default code splitting configuration we mentioned at the beginning splits bundles by page-level. That means any import in a page template will end up in its bundle whether it is actually rendered, used, or not.
81+
82+
## How to Configure
83+
84+
To use loadable-components with SSR, we use a Gatsby plugin, [gatsby-plugin-loadable-components-ssr](https://github.com/graysonhicks/gatsby-plugin-loadable-components-ssr). This makes the webpack configuration we need to use loadable-components while _maintaining SSR_. This is crucial, because if you only code-split, yes the bundle can be smaller, but the client-side rendering will drastically hurt performance more than the bundle reduction could make up. To configure the plugin, follow the [instructions here](https://github.com/graysonhicks/gatsby-plugin-loadable-components-ssr).
85+
86+
## Verification
87+
88+
Once you follow this pattern, how can you test if a component is being code-split and SSR'd? The easiest way is through DevTools.
89+
90+
After building the site and serving locally, refresh the page with DevTools open.
91+
92+
First, verify the code-splitting by looking for a JS bundle in the Network tab with the naming pattern: `component--[my-component]-[hash].js`.
93+
94+
Then, to verify SSR is still working, click the 'All' option in the Network tab and look for the first item `localhost`. This is the initial HTML sent by Gatsby. Verify that you can see the code-split component in this initial HTML.
95+
96+
That's it, happy building! 🎉
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module.exports = {
2+
siteMetadata: {
3+
title: `Gatsby + Loadable Components Example`,
4+
description: `How to code-split at the component level with loadable-components and maintain SSR with Gatsby.`,
5+
author: `Grayson Hicks`,
6+
},
7+
plugins: [
8+
`gatsby-plugin-react-helmet`,
9+
`gatsby-plugin-image`,
10+
`gatsby-plugin-loadable-components-ssr`,
11+
{
12+
resolve: `gatsby-plugin-perf-budgets`,
13+
options: {
14+
devMode: true,
15+
},
16+
},
17+
{
18+
resolve: `gatsby-source-filesystem`,
19+
options: {
20+
name: `images`,
21+
path: `${__dirname}/src/images`,
22+
},
23+
},
24+
`gatsby-transformer-sharp`,
25+
`gatsby-plugin-sharp`,
26+
`gatsby-plugin-gatsby-cloud`,
27+
],
28+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "gatsby-loadable-components-example",
3+
"description": "An example of Gatsby with loadable-components",
4+
"version": "0.1.0",
5+
"author": "Grayson Hicks <[email protected]>",
6+
"dependencies": {
7+
"@loadable/component": "^5.15.0",
8+
"gatsby": "next",
9+
"gatsby-plugin-gatsby-cloud": "next",
10+
"gatsby-plugin-image": "next",
11+
"gatsby-plugin-loadable-components-ssr": "3.0.0",
12+
"gatsby-plugin-react-helmet": "next",
13+
"gatsby-plugin-sharp": "next",
14+
"gatsby-source-filesystem": "next",
15+
"gatsby-transformer-sharp": "next",
16+
"prop-types": "^15.7.2",
17+
"react": "^17.0.1",
18+
"react-dom": "^17.0.1",
19+
"react-helmet": "^6.1.0"
20+
},
21+
"devDependencies": {
22+
"gatsby-plugin-perf-budgets": "0.0.18"
23+
},
24+
"keywords": [
25+
"gatsby"
26+
],
27+
"license": "0BSD",
28+
"scripts": {
29+
"build": "gatsby build",
30+
"develop": "gatsby develop",
31+
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
32+
"start": "npm run develop",
33+
"serve": "gatsby serve",
34+
"clean": "gatsby clean",
35+
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
36+
},
37+
"repository": {
38+
"type": "git",
39+
"url": "https://github.com/gatsbyjs/"
40+
},
41+
"bugs": {
42+
"url": "https://github.com/gatsbyjs/gatsby/issues"
43+
}
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as React from "react"
2+
import PropTypes from "prop-types"
3+
import { Link } from "gatsby"
4+
5+
const Header = ({ siteTitle }) => (
6+
<header
7+
style={{
8+
background: `rebeccapurple`,
9+
marginBottom: `1.45rem`,
10+
}}
11+
>
12+
<div
13+
style={{
14+
margin: `0 auto`,
15+
maxWidth: 960,
16+
padding: `1.45rem 1.0875rem`,
17+
}}
18+
>
19+
<h1 style={{ margin: 0 }}>
20+
<Link
21+
to="/"
22+
style={{
23+
color: `white`,
24+
textDecoration: `none`,
25+
}}
26+
>
27+
{siteTitle}
28+
</Link>
29+
</h1>
30+
</div>
31+
</header>
32+
)
33+
34+
Header.propTypes = {
35+
siteTitle: PropTypes.string,
36+
}
37+
38+
Header.defaultProps = {
39+
siteTitle: ``,
40+
}
41+
42+
export default Header

0 commit comments

Comments
 (0)