- Code and Data Splitting
- Writing universal, "node-safe" code
- Environment Variables
- Building your site for production
- Continuous Integration
- Hosting
- Using a CMS
- Rebuilding your site with Webhooks
- 404 Handling
- Dynamic Routing
- Webpack Customization and Plugins
- Pagination
- Browser Support
React Static also has a very unique and amazing way of requesting the least amount of data to display any given page at just the right moment. React splits code and data based on these factors:
- Route Templates - Under the hood, React Static automatically code splits your route templates for you. Other than assigning templates to routes in your
static.config.js
, you don't have to do anything else! Magic! - Route Data - Each route's
getData
function results in a separate data file for each route being stored as JSON next to the routes HTML on export. This covers the 90% use case for data splitting, but if you want even more control and want to optimize repeated data across routes, you can use thesharedData
andcreateSharedData
api explained below. - Site Data - For data that is needed in every (or most) routes, you can pass it in the
config.getSiteData
function and make it accessible to any page in your entire site!. - Manual Code Splitting with Universal - React Static comes built in with support for
react-universal-component
. This means aside from the automatic code splitting that React Static offers, you can also manually code split very large components if you choose to do so. See the "About" page in the dynamic-imports example to see how it works and how easy it is!
Most projects don't need shared route data. There are cases where it won't make sense to place an individual copy of the same piece of data in every route's getData
function, nor do you want to load that data into every page with siteData
. To solve this issue, you can use the sharedData api to share a single piece of data between many routes with only a single JSON file.
Because React-Static code is both used in the browser during runtime and in node during build and export, it is very important that ALL your code be "universal" or in other words "node safe". Most of us are used to writing javascript from the browser's perspective, so there are some things to watch out for:
- Check before using
window
,document
or browser only APIs. Since these objects do not technically exist in the node environment, make sure you check that they exist before attempting to use them. The easiest way to do this is to keep code that relies on them incomponentDidMount
or inside a condition, eg.
if (typeof document !== 'undefined') {
// use document
}
- Ensure any external libraries that rely on
window
,document
or browser specific APIs are not imported in the node environment. Not all libraries that use these objects require them immediately, but some of them do. If you encounter one of these libraries, you'll see it error when you attempt tobuild
your site for production. To fix these errors, you can stub and require the library conditionally:
let CoolLibrary = {} // you can stub it to whatever you need to to make it run in node.
if (typeof document !== 'undefined') {
CoolLibrary = require('cool-library').default
}
During your adventures, you may need to access specific React Static environment variables in your application. React Static uses the same NODE_ENV
variable that other build systems use to determine what environment you are currently in. In addition, React Static also relies on the presence of document
to determine whether production code is being executed in node or not.
if (process.env.NODE_ENV === 'development') {
// Development mode (only executed in )
} else {
// We are in production mode
}
if (typeof document !== 'undefined') {
// We are in a browser context
} else {
// We are in a node context
}
Before you deploy your site to production, we suggest doing a few things:
- Test your build locally. To do this, you can run
react-static build --staging
. This will build a production version of your site that is still able to be served normally onlocalhost
. - If you find any bugs in production, you can also turn on debug mode adding the
--debug
flag to your build command. This will provide source-maps and better stack traces during testing.
Once you're ready to build for distribution, run the react-static build
command to run a build in production mode. The distributable production files will be located in the dist
directory. Then, simply upload the contents of this directory to your host!
If your static site has static content that is changing often, you may want to set up continuous integration through some sort of service. The most common pairing you'll see is using Netlify with a linked Github repo. This allows your site to automatically rebuild whenever your code changes. How nice! If you have some type of custom hosting solution, you could also look into using Travis CI to build and deploy your site to a custom location. The possibilities are endless!
Deploying a static site has never been easier on today's internet! There are so many solutions where you can host static files for very cheap, sometimes even for free. This is, in fact, one of the great benefits to hosting a static site: there is no server to maintain and scalability is less of a problem. Here is a list of static-site hosting solutions we recommend:
- Netlify (Our favorite!)
- Zeit now
- Github Pages
- Heroku
- AWS (S3 + Cloudfront)
- Google Cloud Platform (GCS)
A content management system (CMS) can greatly increase your ability to organize and contribute. At React Static, we love using GraphCMS, Contentful and Netlify CMS, but you can always visit https://headlesscms.org/ (built with React Static 😉) for help on picking the best one for you!
If you choose to use a CMS, you're probably going to ask yourself, "How will my site rebuild when content in my CMS changes?" The answer is webhooks! Most if not all modern CMSs provide webhooks. These are simply URLs that get pinged when something changes in the CMS. These could be any URL, but are used most productively when they are hooked up to a continuous integration or hosting service to achieve automatic rebuilds when anything in your CMS changes.
Examples:
Making a 404 page in React Static is extremely simple, but depending on your server can be served a few different ways:
- Place a
404.js
react component in thepages
directory. No configuration necessary! - Define a route with the following:
{
path: '404',
template: 'path/to/your/404/component.js'
}
How is the 404 component used?
- Your 404 component is exported to a root level
404.html
file at build time. Most servers will automatically use this for routes that don't exist. - If the
<Routes />
component is rendered on a route with no matching static route or template, the 404 component will be displayed.
Sometimes you may want to handle routes (including sub-routes) that should not be statically rendered. In that case, you can treat Routes
like any other component and only render if when no dynamic routes are matched. This can be seen in the Dynamic Routes with Reach Router Guide, but should be possible with just about any client side react router.
React-Static ships with a wonderful default webpack config, carefully tailored for react development. It should support a majority of use-cases on its own. But, in the case you do need to modify the webpack configuration, you can create a node.api.js
file in your project and use the handy webpack API to extend it!
Please see our Pagination Guide!
React-Static dually relies on lowest common browser support between React itself and your choice of Babel polyfills.
- All latest versions of modern browsers (Chrome, Firefox, Safari) are supported out of the box.
- Internet Explorer is supported, but requires using
babel-polyfill
to work (mainly relying on thePromise
polyfill)
To extend static.config.js
for compatibility with Internet Explorer, first install babel-polyfill
:
yarn add babel-polyfill
Then add the following webpack object to the default export of static.config.js
to extend the existing webpack configuration:
webpack: (config, { stage }) => {
if (stage === 'prod') {
config.entry = ['babel-polyfill', config.entry]
} else if (stage === 'dev') {
config.entry = ['babel-polyfill', ...config.entry]
}
return config
},