⚛️ 🚀 Introducing React-Static — A progressive static-site framework for React!
Last updated on June 26th, 2018
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 similar architecture.
- Aggressive preloading.
- Proper code splitting.
- Mature plugin ecosystem and community.
- Image processing
Things we didn’t like:
- When it came time to introduce dynamic data into our sites we were faced with the decision to use an existing Gatsby source plugin, or build our own. When deciding to build our own, the process and API was very confusing, lightly documented and not as fluid as we hoped it was going to be.
- Gatsby has a great selection of plugins and a system to implement them. This comes in handy for technical implementations like extending webpack/babel/build functionality and we liked that quite a bit. However, when it came to source plugins, we found they were difficult to customize beyond what they expose as configuration options. This made it a friendly platform for users who were tired of ejecting in tools like Create-React-App, but felt constrained for people who wanted more freedom with their data, instead of being forced to use a plugin to import it.
- Colocation of data queries with templates. Similar to Next, this decision introduces a lot of cognitive and processing overhead into the project and builds. It requires that at dev and build time, the data must be rediscovered and queried again from the database by executing all of the queries found in the template files. This confused us as to where the data was coming from or how it was being injected into our components (it is magically placed on the template’s props after the query resolves). 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 more of a central location to manage data and a more explicit way to consume it. We believe our components should be consumers, not producers of data.
- GraphQL or bust. Using external data in Gatsby (at least, the way they intend you to) requires that you know GraphQL. Even if you use an existing source-plugin to import your data, you still have to use GraphQL to query it back out into your templates. If a source plugin isn’t available for your data, you are instructed write your own to ingest your data into Gatsby’s GraphQL schema. This multi-step process required that we know and use GraphQL and Gatsby’s internal schema, even if the data we were consuming was extremely simple in nature. In most of our use-cases, our data was almost always sourced from either a headless CMS (often powered by GraphQL already), JSON files, markdown files, and/or public API’s. For a majority of our use cases, 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 any overly opinionated or proprietary schemas (including GraphQL) 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!