Leading the development of electron-react-boilerplate for over 5 years has taught me quite a bit about performance challenges with electron and pushing the envelope of its performance. Contrary to popular belief, I believe that electron apps can perform as efficiently, if not more efficiently, than native desktop apps (VSCode vs Sublime is a classic example of this).

Before reading this, its important to recognize that applications will certainly benefit a lot more from fundamental architectural improvements as opposed to the optimizations discussed here. If you'd like to read more about architectural improvements, read the slack posts in the "Classic Electron and JS Perf Readings" section below.

Strategies for Performance Improvement

Over the course of maintaining electron-react-boilerplate, I've noted the following strategies as the most impactful improvements to be made to electron apps.

1. Debugging Electron perf using devtools

Applying traditional browser performance debugging techniques to Electron apps is a good starting point. Reading the flame graphs of your application will help you undestand what parts of yor app you should start optimizing. You'll understand what the bottleneck of your application is: loading assests, parsing JS, evaluating JS, etc.

2. Reducing require calls

One of the most significant improvements you can make to an electron app is reducing the JavaScript that is parsed and evaluated by your app. Similar to traditional web development, this can be done by using webpack to split your code into separate bundles and optimize them. This also makes the following step possible.

3. Deferring requires using lazy loading

This can be done in a number of ways. One way of reducing the up front cost of loading your application is to use route-based code splitting and an app shell architecture. While working as a consultant for companies that use electron-react-boilerplate, I've used route-based code splitting optimizations to bring down startup time from ~20sec to ~5sec.

4. Fallback to native node modules or WebAssembly

There are situations where the performance of an application are bottlenecked by computationally expensive tasks. For example, a computationally expensive front end application like Photoshop will likely be bottlenecked by the time taken to render photoshop projects. Games are another example of computationally expensive client side applications. In these kinds of situations, it makse sense to leverage WebAssembly (WASM). WASM serves as a "short cut" to the JavaScript engine's optimizer. Rather than the JavaScript engine having to execute and optimize code at runtime, WASM executables are optimized ahead of time and therefore provide much more deterministic performance characteristics.

In the context of Node, native modules can be used to significantly improve performance. Native modules are modules that written in C/C++/Rust. Node supports running WASM, but WASM trades portability over performance so native moudules will likely perform better than WASM-based modules. Regardless, both should perform better than a purly JavaScript implementation.

5. V8 Snapshots

In addition to lazily loading modules and reducing require calls, another moethod of improving the initial startup time of an electron app is taking V8 snapshots of the initial heap that is created when your application initializes and then reinitializing this heap snapshot everytime the application is started so the heap and the objects in it don't need to be reconstructed every time the application is started. The V8 team wrote a great article on V8 Snapshots, which I highly recommend reading. I'd also recommend checking out electron-link, a piece of electron infrastructure which eases the process of leveraging V8 Snapshots with electron.

📚 More Readings

For thoses that want to read more in-depth articles on optimizing the performance of Electron and browser apps, I recommend checking out the following links.

🙇‍♀️ Case Studies

📖 Classic Electron and JS Perf Readings