When we consider rendering a React application, it’s easy to jump straight to thinking about single-page applications, starting off with an application bootstrap that looks something like this:
Code like the above is rendering a single React application into a page, and letting that app render and manage all the pages’s functionality. Not every website is a single page application, though.
Instead of forcing us to render one top-level component for a page and then making that responsible for everything within, React gives us the flexibility to mount multiple applications to different elements within the DOM on the same page. The page as a whole isn’t a React component, but it uses React to manage interactivity for small subsets of itself.
Predictable state: For certain forms of UI functionality, declarative, state-driven functionality is efficient and understandable. React makes building interactive functionality more testable and predictable than with raw DOM manipulation.
Leveraging a cohesive set of libraries: Front-end developers haven’t had much of a UI library ecosystem since jQuery and similar platforms stopped being relevant. Communities like React’s, however, offer substantially cohesive sets of components that can be useful within the UI layer. Although relying on libraries for UI concerns isn’t always the best choice, having them available to handle small yet complex pieces of functionality — date pickers, for instance — can allow time and effort to be spent on more business-specific functionality.
Common architectural patterns: React supports multiple clear techniques to communicate across component boundaries. Building a component with React pushes us toward patterns that are common to the community and easily maintained by another developer. With non-SPA websites picking up more complex, interactive functionality as the web gains maturity, the benefits of building maintainable software through the use of common and easily-understandable patterns can’t be overstated.
Incremental adoption: If you’re not sure about React as a platform, adding a bit of React code to an existing page can allow for incremental migration toward more adoption of the platform with minimal risk — and allow you to back of that direction if your domain or team experience makes it less desirable once you’ve seen it in production.
Testing: Testing front-end code is hard to do well, and frequently overlooked. I’ve yet to have a more enjoyable experience implementing and debugging front-end tests than with React and some of the tooling commonly paired with it, such as Jest and Enzyme.
Additional bundle weight and script startup time: A large bundle in itself isn’t that big of a deal. Sure, it’s nice to keep download sizes lean, but if a single page application requires a larger bundle weight, that’s the price of entry for higher interactivity. Unfortunately, outside of single page applications, a bundle must be parsed and executed more than once. Your application needs to bootstrap itself every time a user visits another page. In my case, this didn’t lead to an excessive performance penalty, but I’d recommend benchmarking performance for your use case. When it’s necessary to wring out more performance, a leaner React-like implementation that supports JSX can cut down on size, as can avoiding unnecessary polyfills based on understanding of your userbase.
Summarizing my own experience with React in non-SPA websites
Once I took care of the straightforward foundational work — providing mechanisms to find, mount, and initialize components, and providing the data necessary for them to potentially replace a static view of the same content — adopting React outside of a single page application has been an efficient and enjoyable experience. It’s saved me from reinventing the wheel on cross-component communication, and it encourages writing reliable, self-documenting components, particularly when paired with TypeScript and Jest.