I work on Missive, a team email/chat client built with React. It uses a multi-column layout to display conversations. On ⬆︎️⬇ key presses, the app navigates to a new conversation. It needs to do so in as few milliseconds as possible to avoid any user-perceivable delay.

A conversation can include hundreds of comments, emails, and events, all composed of multiple components. To optimize navigation between conversations, we started converting many stateful components into functional ones.

For instance, the Avatar component went from:

… to:

As you can see, a functional component is just a simple JavaScript function that returns elements. Functional components are also referred to as stateless components.

We thought this change would instantly improve performance, because when navigating between conversations in Missive, React unmounts and mounts hundreds of components. You’d think functional components would avoid this mounting / unmounting as well as lifecycle events overhead because they are just functions, but you’d be wrong.

Dan Abramov’s take on functional components

The original React 0.14 release notes confirms this (emphasis mine):

This pattern is designed to encourage the creation of these simple components that should comprise large portions of your apps. In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.

Bummer. React still does a lot of stuff on functional components that, by nature, will never be used. Precious computing milliseconds lost.

What if we could skip React internals for these components? Instead of mounting them as components, let’s just call them as what they really are: plain JavaScript functions. In other words, we just need to change our Avatar JSX tags to braces and call the function directly. Like this:

We just eliminated one big React.createElement call, and all React component lifecycle events! No need to wait for the React team to ship internal optimizations.

To measure the change, I created this benchmark, the results are quite staggering! From just converting a class-based component to a functional one, we get a little 6% speedup. But by calling it as a simple function instead of mounting, we get a ~45﹪ total speed improvement.

As with all benchmarks out there, take those figures with a grain of salt. It was built to reflect Missive’s use case of having to mount/unmount a very high number of components as fast as possible (navigating between conversations).

TL;DR

Directly calling functional components as functions instead of mounting them using React.createElement is much faster. You can do so today in JSX like this:

Simple. Any obvious gotcha we may have overlooked? Share them in the comments.