Generate a Static Website from Your React App

All of us love React, right? At least I do :). The main reasons I love React are, component decomposition and JSX (BTW some people hate it) where you can write HTML codes directly in the JS, and last but not least the performance thanks to its virtual DOM.  

Before I get into the real stuff, let me explain how we have used React Static. In our case, we have a couple of sub-products under the main company ShoutOUT Labs. Therefore, we have to create a couple of websites which almost shares the same layout and some common components. Ex: Header, Footer, Contact us etc… Initially, we didn’t go with React and tried to build static websites with Html and JavaScript since React is another library which also needs to load when requesting the website. We didn’t like it since it affects the loading time. As we know, even milliseconds of delays matter when it comes to modern website development. But, soon we realized that it is hard to maintain since even for small changes in a common component, you have to make changes in a couple of places. So, we decided to go with React as we are comfortable with it and our dashboards have been already developed with React. However, we have our initial concern regarding the performance and best practices. So we did a little bit of Googling and found out React Static: progressive website generator for React, and this article which compares other similar frameworks like Next.js, Gatsby. (I recommend you to read it)

React Static will take care of site loading performance, optimizations, preloads, code and data splitting also the common SEO practices. So you can just focus on developing your site. Also, for optimizing React, we can use Preact which is a 3kb library similar to React. React Static also have many examples including Preact example which you can just start using within a few minutes. Another thing is that you can write the React codes and by the build time, React Static will convert it to a Preact-compatible code.

With that boring explanation, let’s get into the serious stuff.

Install React Static

First, install React Static globally.

yarn global add react-static

Create a project

Then create a project with React Static.

react-static create

This will ask you a project name and an existing template to use. Choose as needed. You may have to wait a few minutes before everything is ready.

Configurations

In the project directory, you will see a file called static.config.js. This is the main configuration file in your project which has details like routes, plugins, compile information and many more.

Sample static.config.js

import axios from 'axios'
export default {
preact: true,
getRoutes: async () => {
const { data: posts } = await axios.get('https://jsonplaceholder.typicode.com/posts')
return [
{
path: '/blog',
getData: () => ({
posts,
}),
children: posts.map(post => ({
path: `/post/${post.id}`,
component: 'src/containers/Post',
getData: () => ({
post,
}),
})),
},
]
},
}

Preact: true means that the build file will be used Preact instead of React.

 

You can specify the routes and relevant component to load in the config as above and even you can prefetch data which are needed to render the page.

If you need other webpack plugins or you need to customize the webpack config, you can do it with webpack property.

As I mentioned earlier in the *boring* part, we have to have a couple of product pages under the main website and each should have its own title, a description which is needed for SEO. The Document property can be specified in react.static config which you can use to override the default export HTML. In our case, we have specified the common meta tags, favicons etc. inside the Document property function.

 

Document: ({ Html, Head, Body, children, siteData, renderMeta }) => (

   <Html>

     <Head>

       <meta name="viewport" content="width=device-width, initial-scale=1" />

       <meta charSet="UTF-8" />

       <meta name="theme-color" content="#ffffff" />

       <meta name="author" content="ShoutOUT Labs" />

     </Head>

     <Body>

       {children}

     </Body>

   </Html>

 )

 

And inside the main product page components, we have imported Head higher order component(HOC) and customized the export head content as needed for the product. So, the different static product pages will have different titles and descriptions as relevant to it.

import { Head } from 'react-static';

<div className="engage">

   <Head>

     <meta charSet="UTF-8" />

     <title>ShoutOUT: Customer messaging and engagement platform</title>

   </Head>

</div>

 

Develop and Live reload

 

You can develop your site with hot reload using the following command.

Yarn start

Build static site

Finally, you can generate the static site with the following commands (staging stage or production build).

Yarn stage

Yarn build

Performance

 

The following is the Lighthouse report of the current development. There are a few more optimizations we have to do in our images. So with that, we could achieve the maximum performance. This is pretty good, right?


Bonus Tip- add favicons to site

 

React static does not include favicons generating. So in our case, we have used another library called @kuroku/react-static-favicons. However, the way we have included it in our site is a bit different than suggested in its Readme due to its performance issues (which is mentioned after the following).

 

Suggested way

 

// static.config.js

import ReactStaticFavicons from '@kuroku/react-static-favicons';

// keeping the instance global allows the favicons result to be cached

const reactStaticFavicons = new ReactStaticFavicons({

    // string: directory where the image files are written

    outputDir: path.join(__dirname, 'dist'),

    // string: the source image

    inputFile: path.join(__dirname, 'logo.svg'),

     // object: the configuration passed directory to favicons

    configuration: {

        icons: {

            favicons: true,

            // other favicons configuration

        }

    },

});

// react-static config

export default {

    renderToHtml: async (render, C, meta) => {

        meta.faviconsElements = await reactStaticFavicons.render();

        const html = render(<C />);

        return html;

    },

    Document: ({Html, Head, Body, children, siteData, renderMeta}) => (

        <Html>

            <Head>

                {renderMeta.faviconsElements}

            </Head>

        <Body>

            {children}

        </Body>

        </Html>

    ),

}

The problem in this approach is the following line inside the renderhtml property.

meta.faviconsElements = await reactStaticFavicons.render();

For every page, subpage, React static will call renderHtml function and therefore, this line will be executed multiple times which then affects the build performance a lot and it is not required. What we did was take the above line out of the renderHtml and store the result of it in a variable. So if the variable contains the results of the previous run, it won’t run the above line again.

Our approach

//outside from static config

 

let faviconRenderResult = [];

const getFaviconRenderResult = () => {

return new Promise(async (resolve) => {

   if (!faviconRenderResult.length) {

     faviconRenderResult = await reactStaticFavicons.render();

   }

   resolve(faviconRenderResult);

 });

 

}

 

//render to html function

 

renderToHtml: async (render, C, meta) => {

   meta.faviconsElements = await getFaviconRenderResult();

   const html = render(<C />);

   return html;

 }

This helps us to save a couple of minutes of build time in our case.

Hope you have something to get out of my article. And for the note, we are in the middle of our site migration with React Static. So you can’t see these things in our current live website.

 

Leave a Comment

Your email address will not be published. Required fields are marked *