This article explains how to scaffold a website using Create React App, hook it up to content from a CMS and deploy as a static website on AWS S3.
React has popularized component based development and risen to dominate the front end space with an intuitive API for building applications. It boasts a huge community and has an excellent developer experience.
React is traditionally used for building client side applications, which are burdened by an upfront performance cost. For websites that do not require a single page app experience, it is irresponsible to make a user pay this UX penalty.
React has more recently introduced server side rendering as a great solution to this problem, however this can require a complex Node server architecture, which may not be appropriate for websites with more basic needs.
We can achieve the same result of sending pre-rendered markup to the browser with a technique called static rendering (also known as snapshot rendering). This is where the pages are rendered into HTML by a build process and then written to static HTML files. This gives us the performance boost without the complex server maintenance. This article will explain one such build process.
Static sites are better for Google indexing and can mitigate security threats by way of not having a database behind the page rendering.
We will start with a basic client rendered app, built from Create React App. Run this command in your terminal:
$ create-react-app static-react
This will install a React application into a new directory 'static-react'. We can
cd into this new directory and
yarn start to see it in our local browser.
Set up routing
Next we need to add some pages, so we can set up routing. Stub out some basic pages with new files
Install React Router:
$ yarn add react-router-dom
App.js with the relevant React Router components, plus the new page files we previously created.
Deploy to S3
This section will briefly describe how we can deploy our website to Amazon S3 using Travis CI. You can choose to use an alternate CI service, such as Bitbucket Pipelines, or just skip the deployment step and follow on with this tutorial using your localhost.
You will need to create a free account with Travis CI which you can do using your Github login. Next we create a
.travis.yml file with the following config:
The last two lines need to be uncommented and updated with the details from your S3 bucket, for example:
bucket: "static-react-website-tutorial" region: ap-southest-2
Initialize the project as a Git repo and commit the files. Create a new repo on Github and push up the initial commit. We must then sync Travis CI with our Github account to pick up the new repo. Now, every time we push a new update to Github, Travis will
yarn build then deploy the compiled code to S3.
We can now view the site at http://your-bucket-name.s3-website-your-region-name.amazonaws.com. If we open the dev tools and select the "Network" tab, we can record a timeline of the page render. Since it is important to optimise for 'mobile first', we will select "Fast 3G" from the network speed presets, then record the page load.
The above screenshot shows that nothing renders until 1.50s, with the complete render clocking in at 2.09s. We should keep in mind that this website has a bare minimum of content, so it is taking 2 seconds to render hardly anything. In the next section, we will improve this with static rendering.
Set up Static Generator
Install the react-snap package
$ yarn add react-snap
Update the build script in
package.json to run
react-snap after the app build
Update index.js to use the React 16
We can now push the updates to Github and trigger another build and deployment. Then we can rerun the page load performance test with dev tools.
Set up CMS
$ yarn add contentful
Next, create an account with Contentful, set up a new space, and then navigate to where it displays your API keys for the space. Create a
.env.local file and add to it the "space ID" and "access token".
Then create a handful of content items so we can consume them in our site.
Next, we update our
Home.js file with a fetch to the Contentful API for the posts we just created. The updated file is now this:
When we run this in our browser, we'll see the object for each entry in our console, as shown below. We can inspect this data structure to determine which object keys to use for displaying the relevant data.
Let's now make each of these entries link to their respective pages. Update
Home.js with React Router's
Link. This is the diff to the previous snippet:
We now need to update the Post route in
App.js accordingly, and also remove the placeholder links. The diff for this is:
We should check these links are working in the browser - click each of the links rendered on the homepage and we should see the same placeholder content loading for different URLs, each containing the post id.
Next, we will wire up the content for
Post.js. We will import the Contentful client and make a fetch to their API using the post ID from the URL, which we can access via React props
this.props.match.params.id. The updated file is shown below:
Again, we start by logging the data object to the console to see what we get back from the API:
Now, let's update the returned markup with the post content. The diff for this is:
A critical part of static web pages is the presence of metadata such as the
<meta> attributes for SEO and social sharing. Create React App does not include any features for this out of the box, so we will introduce the popular React Helmet package to manage our metadata.
$ yarn add react-helmet
Let's start by updating
Post.js to render a
<title> attribute containing the post title. The diff for this is:
Contentful supports Markdown syntax out of the box, so in order to take advantage of this feature, we need our React templates to support rendering Markdown. Firstly, let's add some Markdown to one of our posts in the CMS, for example:
In this tutorial we will use the marksy package, a library for converting markdown into a virtual DOM structure.
$ yarn add marksy
We can now update
Post.js with a helper function for compiling the raw Markdown into a VDOM structure to output inside our JSX. The diff for this is:
Conclusion and Starter Repo
We have covered a lot of ground in this tutorial, taking a basic client rendered Create React App installation from its default state to a CMS-powered static website with Markdown and metadata support.
To make life a little easier, I have created a starter repo including all the packages featured in this tutorial, configured to generate a static website rendering the example content from Contentful's blog content structure. You can find this on Github here: https://github.com/astrotim/contentful-static-react. Please feel free to submit bug reports or feature requests via a Github issue.
Now go forth and create static!