In this post, I'll show you how to pre-render a normal React app (created with create-react-app) into static HTML, and I'll also show you how to add meta tags to your page to help with SEO and SMO.
React-Snap is a tool use to "pre-renders a web app into static HTML. Uses Headless Chrome to crawl all available links starting from the root." We'll use it to pre-renders our React app into static HTMLs and serve those HTMLs via Express.js.
Advantages of serving static HTML files is that it works better with SEO and SMO.
Google search bots can more easily crawls through static HTML pages. While, the bot can also crawls through client side javascript rendered web site, it might take a longer time, and might not work as expected if the web side javascript app doesn't build properly.
Statc HTMLs with the help of react-helmet allow the web site page to be share with images and page descriptions to social media site.
In the react project root directory, install the react-snap dev dependency.
Using npm:
0
$ npm install --dev react-snap
Using yarn:
0
$ yarn add --dev react-snap
Then add a "postbuild"
script to to your react package.json's scripts property:
package.json
0 1 2
"scripts": { "postbuild": "react-snap" }
And in your src/index.js
(for React.js 16+) change to the following:
index.js
0
1
2
3
4
5
6
7
import { hydrate, render } from "react-dom";
const rootElement = document.getElementById("root");
if (rootElement.hasChildNodes()) {
hydrate(<App />, rootElement);
} else {
render(<App />, rootElement);
}
To build and pre-render the app into static HTMLs run in your react project root directory. The postbuild script will run automatically after the "build"
script.
0
$ npm run build
This will create static HTML files inside the build/
directory. With react-snap, 200.html
will be use as the new index.html
.
While pre-rendering my own website using react-snap, I've found that the Link
component of react-router
doesn't work as expected. react-snap uses Headless Chrome to crawl through each route of the React app. In my website, I've use the Link
component to pass a state to the routed page:
0
1
2
3
4
5
6
7
8
9
render() {
return (
<Link to={{
pathname: '/posts/generate_static_site',
state: { state0: "0", state1: "1" }
}}>
...
</Link>
)
}
Headless Chrome will render the passed state as undefined. Therefore, I had to use other ways to get additional data from the Link
route. What I've come up with is to put the data in a separate file instead and get it using the Link url parameters:
Data.js
0 1 2 3 4 5 6 7 8 9 10 11
export default (id) => { return data[id]; }; const data = { "id0": { data: "Hello there" }, "id1": { data: "General Kenobi" } };
RoutedPage.jsx
0 1 2 3
componentWillMount() { const { postname } = this.props.match.params; const { data } = GetData(id); }
You could also store your data inside a database, and query it via an API request to your backend RESTful server.
React-Helmet output the meta tags from React.js pages into HTML files. This allows the HTML files to be serve with the meta tags for rich link preview (sharing on social media sites).
In you react project root directory run the following to install react-helmet
:
Using npm:
0
$ npm install --save react-helmet
Using yarn:
0
$ yarn add react-helmet
0
1
2
3
4
5
6
7
8
render() {
return (
<Helmet>
<title>Home page</title>
<meta name="description" content="This is simply the best home page there is!"/>
<meta name="og:image" content="homepage.jpg"/>
</Helmet>
)
}
The title
will be display on the browser tab as well as on the link preview on social media. The image and the description will also be on the link preview as well.
I serve my web site using Express.js. You can serve it using other methods, but in this post I'll show you how to serve it using Express.js.
index.js
0 1 2 3 4 5 6 7 8
const app = express(); app.get('/', (_, res) => { res.sendFile(path.join(__dirname, 'build', '200.html'), (err) => { if (err) { res.status(500).send(err) } }); });
The changes that had to be made were:
index.html
on /
path, serve 200.html
instead./*
to /
. This is require to change from client side routing to server side, to serve the pre-rendered static HTML files.I hope that you've learned something new today in this post. As I continue to improve my web site, I'll make posts on the improvements I've made to share my experience and journey, as well as to keep as a record of how my web site changes.