⚛️ 🚀 Introducing React-Static — A progressive static-site framework for React!
At Nozzle.io, we take performance and user/developer experience very seriously. We’ve built and launched tons of sites using different static site tools that claim to solve the worlds problems, but we had yet to find one that satisfies our wildest dreams. While we highly appreciate the following libraries and the insight they provided us, our evaluations left us wanting more. Here’s our review:
Next started as a “minimalistic framework for server-rendered React applications”, then later announced support for exporting to a static site. Given the massive amount of stars it had acquired on Github and the fairly quick and easy setup, it seemed very promising. When it comes down to its isomorphic functionality, it’s hard to match. But when it we started using the static export features, we were dropped on our faces with a very ungraceful transition from isomorphic to static that came with a cold side of faulty hot-reloading, strange routing strategies and highly bloated bundles:
Things we liked:
- Very easy to get started
- Well documented
- Large Community
Things we didn’t like:
- Great experience for isomorphic apps, but experience for static functionality & static hot-reloading was second-class (most of the time, hot-reloading didn’t work at all for static routes).
- Colocation of data requirements with templates. (Much more on this below, but a short preview: it seems like a good idea, but ends very badly for DX and performance)
- Large JS bundles and too many of them! We found that every route contained a JS bundle which is a lot of unnecessary duplicate code being generated and downloaded. Usually code-splitting operates on the template level, producing very small and bite-size chunks of code to be progressively imported as you navigate, but for some odd reason, Next gave us a massive bundle not only per template, but also per route. That’s a ton of webpack-ing and anything more than what’s necessary is too much for our taste.
- Destructive routing. In our experience this produced tons of needless component rerendering and shoddy lifecycle purity between pages. We found that even layout-level components had a difficult time retaining state during navigation. At the bottom line, it simply didn’t behave as you would expect a react app to behave.
- Exporting was slow. We found that this was mainly due to colocation of data in templates combined with a tree-walking SSR strategy. Also, Next’s requirement of creating a hefty JS bundle per route instead of per page template brings the performance to its knees.
- Difficult and complex codebase. We are open source developers, so naturally we want to contribute to the projects we use and we even tried! Next’s codebase is unfortunately very complex for a static site generator, mainly due to its dependency on supporting it’s main isomorphic use-case. This made it a very non-trivial task when we wanted to further the development of its static-export capabilities.
Gatsby is a very popular static-site generator for React. Some very notable sites like reactjs.org and segment.io/blog are built using Gatsby, so it’s definitely a library to be taken seriously. We, like many others, were drawn to it because of it’s specific focus on static sites, better build speeds and navigation performance. To say the least, it was 10x better than using Next. After using it for a few weeks, we ended up much closer to where we wanted to be, but not without a few annoying bumps in the road:
Things we liked:
- Very fast browsing & exporting. This served as the inspiration for React-Static’s very similar architecture.
- Aggressive preloading
- Proper use of code splitting.
- Mature plugin ecosystem and community.
Things we didn’t like:
- Great with plugins, but difficult to customize. Beyond what the plugin ecosystem provided us, tweaking Gatsby’s knobs was a chore. Any and all knobs rely on the plugin system itself either as a node module or a project plugin. While this makes it a friendly platform for casual users or those who fall into the use-cases of existing plugins, it feels pretty heavy handed writing a plugin or configuring a plugin get the results you need.
- Colocation of data requirements with templates. Similar to Next, this decision introduces a lot of overhead into the build process. It requires that during server-side rendering, you must continuously render and walk the react component tree, calling custom lifecycle events as they arise and waiting until all of their dependencies resolve. In the microcosm of a component, most think this colocation makes reasoning about component data easier, but at the larger level, it ends up diluted the abstraction point for managing data from a central location. We wished we had this, because we firmly believe our components should be consumers, not producers of data.
- GraphQL or bust. Using external data in Gatsby requires that you use an existing source-plugin (or write your own) to query and ingest your data into an intermediary GraphQL database. Once imported, you can then query your data again, from your templates using GraphQL. This multi-step process required that we use GraphQL and Gatsby’s internal schema, even if the data we were consuming was simple in nature. In most of our use-cases, our data was almost always sourced from a headless CMS (often powered by GraphQL already), JSON files, markdown files, and/or public API’s. We didn’t see any added value in our ability to supply data to pages with this process and grew tired of massaging and shaping our external data to fit into Gatsby’s schema.
The Post-Mortem Realization
We finally knew how much we had been spoiled building enterprise apps with React and it was very clear we needed a tool that behaves more like an SPA and less like an isomorphic rendering framework. We desperately wanted to ditch proprietary schemas and GraphQL dependencies and finally put the kibosh on component-colocated data queries.
The flow of data from your site’s source to finished product should be dead simple and give you all of the flexibility you need.
The tool we started architecting from these experiences made us feel more confident than ever that we could achieve a better experience for both users and developers, and bring the freedom we took for granted building React SPA’s to a static-site-generator.
Introducing React Static!
A progressive static-site framework for React, designed to deliver an amazing experience for your users and developers alike, without compromising React in any way. It’s insanely fast, SEO-ready, and is the most React-friendly static-site library on the planet.
Let’s get to it.
How is it different?
Contrary to most other static site generator for react, React-Static is very direct with how data flows from its source to a route. Not only does this provide a convenient abstraction point for managing all of your static data, but by keeping the data pipelining and react templating as separate as possible, your site can be built as a “function of state” with only a single pass of SSR!
- All of the data your site needs to render is gathered up-front in a configuration file using any means you want. This data can come from Markdown files, headless CMS's, Graphql endpoints, public API’s... anywhere!
- Next, you generate a list of every route on your site, define a template to render it, and supply the appropriate data to each one. It’s as easy as passing props!
- Using React-Static’s components like
SitePropsyou can access the data for each route and write your app as you normally would.
- React-Static can then export every page in your site with tenacious speed and accuracy.
- Upload your site to your favorite static-site host or CDN!
- On the user’s first page, the statically exported HTML and CSS are downloaded to show the content as quickly as possible.
- Simultaneously the bare minimum of JS is downloaded for React to bootstrap on top of the rendered HTML.
- As soon as possible, the rest of the website is optimistically pre-fetched and cached via code splitting and data splitting.
- All further navigation is instantaneous!
What this means for your users:
- Instant browsing & instant gratification. Your site works at the speed of React, and there is no waiting for a page to load
- Full throttle React interactivity. You could build a full blown SPA for your users while still taking advantage of SEO and the progressive/distributed super powers of a static-site.
What this means for your developers:
- Lifecycle Security, Route Animations & more! Under the hood,
react-routerv4 and never uses custom lifecycle methods, which means there is absolutely no
dangerouslySetInnerHTMLor any other destructive methods being used for navigation. It’s just good old React, so you can thoroughly rely on everything functioning how you would normally expect it to, including component lifecycles and state managers.
- Data & Query Agnostic. You’ve worked hard enough producing and organizing all of the data for your website, so the last thing you need is a superfluous GraphQL layer or custom component lifecycle lodging itself between your data and your pages. With
react-static, supplying data to your routes is easy. Just fetch what you need and pass the data to your routes all in once step. You can use various
react-staticcomponents to consume this data anywhere in your site (render props, HOC’s, we got it all!). By moving the data ingestion, querying and prop mapping all into a single location, you write 3x less code for your data pipeline and have a central location to manage the shape of your data.
- Lean builds for today and 2 years from now.
react-static’s approach to data-splitting is very unique and ensures that every route’s template and data is optimized and served as a materialized and distributed API. This means that your site can scale to tens of thousands of pages and still load instantly. Again, since all data fetching is performed at the same single stage,
react-static’s build times are unbelievably fast.
Ready to see it in action?
We have a growing list of sites that are built with React-Static!
Get started in 60 seconds
react-static as a global:
yarn global add react-static
$ npm install -g react-static
Create a new project:
$ react-static create
Follow the instructions and start hacking!