Universal React: You’re doing it wrong

So you’ve got the React basics down, and you’ve decided to put together an app. Given that React runs on both the server side and the client side, it seems that making a Universal (or Isomorphic) app is a no-brainer. Or it did until you’d spend hours trying to figure out how to set up Node.JS and Express, and make it distinguish between requests for JSON and HTML. And that’s if you’re lucky enough to not already have a server written in Rails or Java – at which point you’re probably considering rewriting your entire backend. But wasn’t Universal supposed to be a no-brainer?

While rendering server-side certainly has its benefits, they are by no means… universal. So the question is: should your app be Universal?

And the answer is: probably not. Universal apps introduce a huge amount of complexity. They require you to spend more of your time on maintenance, and less on features. And this isn’t to mention the limitation of requiring your app to be served by Node.js.

Of course, there are cases where this complexity may be justified. In particular:

  1. If your app’s content needs to be indexed by Google, it is valuable to be able to control the HTML which Google sees. Rendering server-side allows you to do this.

    Edit 19th March: Google Developer Advocate Paul Kinlan wrote to say that this is no longer the case – Google now crawls JavaScript-rendered content just fine. Even one less reason to write Universal apps.

    Edit 21st March: Christoph Pojer from Instagram tweeted that at they’re still finding it useful for SEO.

  2. If you’re at the point where every millisecond of page load causes a measurable decrease in revenue, server-side rendering can make things appear on your user’s screens faster.

  3. If you have important users whose device is too under-powered to support JavaScript, Universal may be your only option.

In short, Universal apps make sense when they solve a real problem. They’re not the goal in and of themselves.

Common objection #1: But Facebook is Universal

Yes, and you’re (probably) not Facebook. What makes sense for them doesn’t necessarily make sense for you. Do you have millions of users and thousands of engineers?

Edit 21st March: Dan Abramov tweeted that Facebook does not actually use Node-based server rendering. And while Instagram uses server-side rendering for SEO, they find it does not have a measurable impact on User experience.

Common objection #2: But my React starter kit is already Universal

Great, so you can save a few hours while setting things up. But you’ll also lose a few hours figuring out what all the extra code actually does. And that’s before you lose weeks on maintaining a completely superfluous Node.js app.

Common objection #3: But Universal apps are better

Sure, I’ll let you have this one. Universal apps load faster, support more devices, and provide a better user experience. But this all comes with an opportunity cost.

What other features could you have added with the time you spent on your universal app? What other performance tweaks could you have made? How much earlier could you have actually shipped?

I know it is a little cliche, but the best apps are the ones which actually exist. So if you’re thinking of creating a Universal app, do yourself a favour and make sure you actually have a bloody good reason. Otherwise, treat yourself to a beer or something. Given all the time you’re saving yourself, you deserve it.

And once you’ve done that, join my mailing list to make sure you hear about other beer-worthy time saving tips. You’ll also receive three bonus print-optimised PDF cheatsheets – on React (see preview), ES6 and JavaScript promises. Hell, that is worth a beer in itself!

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

Have something to say? I love hearing your questions and offers! Leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!

Other things you don’t have to do:

Related Projects

19 Comments Universal React: You’re doing it wrong

  1. Šime Vidas

    I’m not a React developer, so I’m not informed, but if it’s really that complex and time-consuming to set up server-rendering in React, then that’s something that needs to be fixed asap. As you say, it makes apps “load faster, support more devices, and provide a better user experience”, so you must see that it’s not a “nice to have” feature, but something that should be a standard for web apps. I’d go as far as to say, that it’s a critical quality of web apps.

    I get that you want to give good practical advice, but if the advice is that it’s probably not a good idea to invest time in serve-rendering, than that’s a severe limitation of React “ecosystem”, imo, and we as web developers have the responsibility to point that out, at any given opportunity.

    Reply
    1. Pavol Fulop

      I started using react not even a month ago, and I would say yes it is back then, now I’d just say not that much.

      The main problem is, there is no definitive way how to do it, so you either grab boilerplate as author said or try yourself and try to figure out the best way.

      I took the simplest boilerplate and tried to understand it. It took couple hours and then it took even more trying to put something I need into it.

      I would say first universal is hard and really time consuming.

      Reply
    2. Erick Maynard

      The performance increases you gain 99% of the time aren’t even worth it. And when using it in situations where you would actually utilize the performance increases, even the front end code can get so complicated that it can be difficult to remember where everything goes, especially the data. Having to pass your data from the top component all the way down, sometimes through a half dozen to a dozen children, get exceedingly tedious and confusing.

      Reply
    3. Dylan Piercey

      I think the configuration required to make isomorphic javascript work is ridiculous so I made a framework that abstracts most difference between client and server side code so you can just write JavaScript.

      If any one is interested in a modern isomorphic routing solution that is similar to express and alleviates much of the headache I recommend you checkout https://rill.site.

      Reply
  2. MichaeL J. Ryan

    You make a couple of assumptions… First of which is that universal react code is any harder, or significantly more difficult to learn than any new framework for server-side development. It isn’t.

    The second is that node is significantly more difficult to run in production than, Java, in your example.

    I feel that you’re wrong on both counts… I’m not saying your conclusion is wrong… most of the time, “universal apps” when you’re looking for an SPA experience isn’t worth it, or needed… but your assumptions are very wrong.

    Also, even though google does JS rendering as part of it’s search, it tends to lag significantly behind it’s non-js rendering (usually days), if your leading content is time sensitive, you still want server rendering (universal or not).

    Reply
    1. Šime Vidas

      Hey Michael, could you give an example of when the content is not time sensitive? I’m having a hard time understanding that kind of thinking.

      I think, it has been established that users increasingly close apps which take a long(er) time to load; hence, Google’s Chrome team is promoting a rule of “1 second to first render”. This goal is hard to reach on mobile without server-rendering, even on a good connection.

      With that being said, I don’t see how server-rendering can left be out of the picture, or only included conditionally, for “time sensitive” content.

      Reply
      1. James K Nelson

        Here are a few examples of content which is not time sensitive:

        • Intranet apps
        • SaaS apps hidden behind login screens
        • Apps which are not generally arrived at via search

        Or in general, apps are probably less time sensitive than pages. And a lot of React code is for apps.

        If you’re making something which *is* exposed to the public via search, I don’t think Universal is really such a bad idea. That’s why I mentioned the exception of where page load time causes a measurable decrease in revenue. The key here is measurable. While the benefit is probably pretty obvious on public-facing content, I’m not so sure it is on the apps a lot of people are building.

        Reply
  3. Ali

    This was great – especially for people like me that might be overwhelmed by feeling the need to learn everything at once instead of letting the pain points present themselves organically. Still find your articles some of the most educational out there. Good job.

    Reply
  4. luca

    Thanks for the article. Although you need to keep in mind that once you have your boilerplate set up, creating an universal application is really easy and takes literally minutes to set up.

    Reply
  5. Daniel Steigerwald

    You are describing isomorphic (client and server) apps, not universal (plus desktop or native mobile). Yeah it’s not easy to set it, but once you have universal stack, it’s pure love. Check my Este, code shared between all platforms easily, no magic. As for Google indexing, parsing static HTML will be always faster than running virtual browser to parse JS generated HTML, so Google will index it much faster. Btw, check este gulp to-html task. Server side rendering is even convenient for static serverless sites / app because all that DX (hot reloading, configuration, etc.)

    Reply
  6. Jack Callister

    It’s nice to see some opposing ideas being floated about. After all React’s mantra is rethinking best practices. It seems easy to get caught up in the idea that your application should be a fully JavaScript rendered SPA without considering the user requirements and technical overhead.

    I’ve fallen into this trap and regretted it as working with server side templates is often a lot easier, reliable and faster – especially for non-interactive components. With that lesson I now find myself working with smaller “speckles” of interaction across the page and push JavaScript components to the edge rather than make them the focus of the application. Often that doesn’t require server side rendering but if it does it’s usually simple to set up (with Node) if not passing down a data payload and rendering client side is quite sufficient.

    Reply
  7. abac

    This means you would serve the same file regardless of route (a .html file with basic html and a body with a div where the app should render)? IF so there’s more issues, like if you want to set different sharing attributes for different urls, and different title/descriptions in your head.

    Reply
  8. Chad Ostrowski

    I agree with a much of this. Thanks for writing it. An objection you didn’t mention: JS-rendered apps can’t have customized metadata & “og:image” tags & such on each page. If a product wants to promote sharing of their content, and wants it to show up well when links are pasted into Twitter, Facebook, Slack, whatever—these are a must. This was actually THE reason I invested a month in turning https://entire.life into a Universally Rendered app. Those meta tags are an important part of viral marketing!

    Reply
  9. Alan

    You misunderstand the benefits of server-side rendering, but I don’t blame you: most people do.

    Because client-side frameworks have trained us to accept server-side rendering as an impossibility or at best a crutch, this isn’t surprising. But before the rise of client-side frameworks (i.e. before “Web 2.0”, when interactive JS was often just called “AJAX” or even “DHTML”) it was unthinkable to serve a bootstrapper/placeholder page and then have the actual code kick in and render the page.

    It’s true that server-side rendering isn’t strictly necessary to build a React app. It’s also true that there are scenarios where server-side rendering provides no tangible benefit at all (e.g. an intranet application exclusively used by desktop users with a stable and fast connection).

    However unlike previous attempts for Angular or Ember, React server-side rendering is not just about “pre-rendering” or avoiding placeholder pages. It’s not simply about SEO. It’s not about users who intentionally disable JavaScript or who use archaic browsers that don’t fully support the baseline level of JavaScript your app requires.

    You need to think of server-side React separately from client-side React. React server-side rendering is a perfectly valid substitute for traditional server-side templates. Instead of generating and updating a DOM tree like React for the browser does, React for the server generates HTML markup. The only difference between ReactDOM vs ReactDOM/Server and ReactDOM vs ReactNative is that ReactDOM/Server is almost completely a strict subset of ReactDOM, just with a different output.

    You’re not “rendering your client app on the server”, you’re building an app that works both on the server and in the client. That’s why it’s called “isomorphic” or “universal”: it’s an app that uses a single code base (with possible branching points or multiple entry points) to run either in a browser environment (rendering into the DOM) or a server environment (generating HTML).

    This means you can have a hybrid between a traditional server-side application and a client-side application. You can have all the flexibility and responsiveness of a client-side application without giving up on the reliability and universal performance of a server-side application.

    If you don’t live in the Bay Area microcosm and if you ever need to use apps on the go, flaky Internet connections are a reality. They are the weak spot of client-side apps — even “offline first” apps only work if your connection is stable enough to fully load them once: great if you can prepare but lousy if you’re starting with an empty cache. Server-side apps work fine in these situations (aside from universal problems like large images/videos/fonts), they have worked fine and they will continue to work fine.

    Of course the key phrase here is “hybrid”: you can make apps that both provide the optimal server-side and the optimal client-side experience and these have always been the holy grail since the idea of AJAX was born, but for most apps there is a point of diminishing returns. Thankfully with React server-side rendering per se is so easy to achieve that this isn’t a concern. But interactivity wise it might not be strictly necessary to replicate every single possible client-side interaction on the server.

    Just use a universal router that allows server-side prefetching. Or even one that doesn’t. Server-side React isn’t an all-or-nothing decision. Just don’t pretend your app is immune to the problems of client-side applications because you’re developing for the optimal conditions.

    Reply
  10. Michael

    Although I generally agree with you that universal doesn’t make sense for many use cases; why do you hate on node so much?

    Reply
  11. eugene kim

    I’m confused.

    If server rendering helps page loading, I don’t see why Facebook doesn’t do it.

    Are you implying that facebook server is merely an api server where client will fetch data from and combine the data with static assets such as HTML, js, css?

    Reply

Leave a Reply

Your email address will not be published.