Snappy server-rendered web app with Rails/Webpack/React/Flux
Rails
Development
There are two main tradeoff options web application developers have to think about today:
Interactivity
For most web applications in 2015 that have a non-trivial amount of interactivity, client-side rendering and routing becomes inevitable. While solutions like Turbolinks/pjax can work well for some (and are always preferable in those cases), many web applications exceed that abstraction and roundtrips to the server cost valuable time between interactions. If you want to get as close to a native experience as possible, client-side rendering (and client-side routing) is the way to go.
Time to first interaction
Unfortunately, the push for client-side rendering caused many web developers to disregard the amount of time until your web applications becomes usable after fetching/parsing/rendering. No one likes to stare at a blank screen for up to 5s before being able to do anything. For some apps this might be less critical (productivity apps a la Gmail, Google Docs come to mind), but for many web applications in 2015, this is a no-no. Twitter famously pushed back to server-side rendering when optimizing for their time-to-tweet
metric. Another concern for some applications may be SEO which is also difficult to obtain with client-side rendered apps.
Often times, these two objectives are seemingly at odds with each other, with the perception often being that you have to choose between either server-side or client-side rendering of your apps. Having the SEO and time-to-first-interaction benefits of server-side rendering combined with the performance and interactivity of client-side rendering is the "holy grail" but has historically come with a significant overhead in development, especially the sharing of UI templates on both the server and the client, as well as a comprehensive state management solution that works across that boundary. However, the web development community have made significant progress on that front, see for example Airbnb's efforts, Ember FastBoot or the many projects around isomorphic react/flux.
An Example
However, as of right now, there has yet to be a good example/tutorial of how to write such "holy grail" web app with established tools and best practices. Which is why I created the following tutorial, whose code you can find at https://github.com/nambrot/rails-webpack-react-flux and a demo instance running http://rails-react.nambrot.com/.
It's a very simple blog, yet offers the following features:
- Fully server-rendered HTML, meaning you can actually use the application without JS enabled.
- Minimization of unnecessary data fetching, as in embededing relevant data in the response as JSON and loading additional data if necessary
- Once loaded, everything going forward is client-side rendered and cached, making it incredibly responsive.
I used Rails as my favorite web framework and combined it with React and Flux on the client side via Webpack to render your UI on the server. I want to give credits to others you have tried to make React work with Rails, namely Justin Gordon's Repo and Kevin Old
Most Rails/React/Flux examples out there are usually lacking in two departments:
- They are "just" TodoMVC, i.e. don't concern themselves with persistence to a real server (e.g. with CRUD) by using localstorage.
- SSR examples with React usually only concern with spitting out HTML without addressing a data management strategy a la Flux. Most will return HTML but require a second rountrip to initialize the Stores. When they do, they usually load the whole collection in, which isn't so great.
I'll try to keep this as concise as possible in a step-by-step guide of how I approached the topic. I'm starting with a very basic Rails Blog that we will gradually "holy-grailify". The following links all points to commits with hopefully helpful commit messages.
1. Introducing Webpack and NPM
Love the asset pipeline to death, but the lack of true modules definitely gets noticable with larger amounts of client side code. We will be using Webpack to allow us to write modular code as well as use the great diversity of the NPM ecosystem, with easy compilation for client-side assets as well as hot-loading
2. Setup basic React and Flux
We are going to set the barebones of the Flux architecture without thinking too much about the server part, for now, we will just fetch everything on demand. That includes setting up the basic App structure
3. Store Deserialization
The first step to server side rendering is to be able to deserialize data into the store for the client. This also avoids the inital request for data. React is also smart enough to not touch the DOM as the resulting HTML is identical.
4. Add Server-Side Rendering
We are going to use a simple express server which will take the 1. route and 2. serializedState as parameters and simply return the HTML. The result is complete HTML pages being returned. In fact, you should now be able to navigate the page without the need for Javascript enabled, time-to-render on the client is faster, obvious SEO benefits. The downside is a "round-trip" to the express server
Conclusion
As you can see, building the "right" web application is not trivial, there is still a lot of decisions and configuration to be made. While the benefits are great, the cost may not be justifiable for many apps and should be carefully considered with the above trade-offs. I for one am excited for Ember's FastBoot that seems to be the least amount of effort to get the benefits.