How to Speed Up an Electron App Startup from 10 seconds to 1 second

This article explains how to measure, analyze, and optimize the startup performance of an Electron‑based cash‑register application, covering main‑process timing, renderer white‑screen reduction, code‑splitting, tree‑shaking, require caching, V8 snapshots, and user‑perceived improvements such as skeleton screens.

Goodme Frontend Team
Goodme Frontend Team
Goodme Frontend Team
How to Speed Up an Electron App Startup from 10 seconds to 1 second

Background

Some cash‑register terminals run Electron apps that start slowly because of old hardware, low‑end configurations, or antivirus interference. The goal is to make the app launch quickly even on such machines.

Optimization Ideas

Measure to set a concrete target and identify bottlenecks.

Focus on reducing the time to create the main‑process window and to display the renderer as soon as possible.

Make the experience feel faster after the performance improvements.

Report timing for each stage and set up monitoring to catch regressions.

Measurement

Measuring the Main Process

Create a batch file in the app root to launch the program and record the initial start time:

@echo off
set "$=%temp%\Spring"
>%$% Echo WScript.Echo((new Date()).getTime())
for /f %%a in ('cscript -nologo -e:jscript %$%') do set timestamp=%%a
del /f /q %$%
echo %timestamp%
start yourAppName.exe
pause

Within the project you can log main‑process milestones using:

this.window.webContents.executeJavaScript(`
  console.log('start', ${start});
  console.log('onReady', ${onReady});
  console.log('inCreateWindow', ${inCreateWindow});
  console.log('afterCreateWindow', ${afterCreateWindow});
  console.log('beforeInitEvents', ${beforeInitEvents});
  console.log('afterInitEvents', ${afterInitEvents});
  console.log('startLoad', ${startLoad});
`);

If abnormal delays appear, use v8‑inspect‑profiler to generate a flame‑graph.

Measuring the Renderer Process

Log timestamps via console or the Performance API, and use Chrome DevTools to measure white‑screen time.

Results

In the simplest case (only window creation logic) the timestamps are:

exe execution          1677666141619
start main code        1677666142152 (+533 ms)
main ready             1677666142224 (+72 ms)
init renderer window   1677666142364 (+140 ms)
load renderer assets   1677666142375 (+11 ms)

In the unoptimized state the main‑process delay between exe launch and code start is about 2.8 seconds, and the renderer white‑screen is ~1 second.

The optimization target is to match the simplest case by reducing main‑process start time and renderer white‑screen.

Optimization Steps

1. Make Main‑Process Code Execute Faster

Package, compress, and enable tree‑shaking to keep the bundle size minimal; load dependencies on demand.

Code Compression

Electron ships with a recent Chrome, so you can target es2020 without polyfills.

Tree‑Shaking

Avoid attaching configuration objects to global so they can be eliminated during bundling.

const exendsGlobal = {
  __DEV__,
  __APP_DIR__,
  __RELEASE__,
  __TEST__,
  __LOCAL__,
  __CONFIG_FILE__,
  __LOG_DRI__,
  GM_BUILD_ENV: JSON.stringify(process.env.GM_BUILD_ENV),
};
// Bad for tree‑shaking
Object.assign(global, exendsGlobal);

Avoid Heavy Shortcut Registration

Registering global shortcuts adds overhead; replace with a triple‑click handler or defer registration.

globalShortcut.register('CommandOrControl+I', () => {
  this.window.webContents.openDevTools();
});

Optimize require

Measure require time with:

node --cpu-prof --heap-prof -e "require('request')"

Typical timings (ms): fs‑extra 83, event‑kit 25, electron‑store 197, electron‑log 61, v8‑compile‑cache 29.

Strategies:

Bundle required packages (increases bundle size).

Load packages on demand.

Use v8‑compile‑cache or V8 snapshots.

Reduce startup transactions to lower overall require count.

On‑Demand require Example

const noop = () => {};
const proxyFsExtra = new Proxy({}, {
  get(target, property) {
    return new Proxy(noop, {
      apply(_, __, args) {
        const fsEx = require('fs-extra');
        return fsEx[property](...args);
      }
    });
  }
});
export default proxyFsExtra;

Measured improvement: main‑process start time reduced by ~700 ms.

v8‑compile‑cache

Cache compiled bytecode to skip recompilation. In this project the benefit was negligible because the initial require count is already low.

Module.prototype._compile = function(content, filename) {
  // read cache
  var buffer = this._cacheStore.get(filename, invalidationKey);
  var script = new vm.Script(wrapper, {
    filename,
    lineOffset: 0,
    displayErrors: true,
    cachedData: buffer,
    produceCachedData: true,
  });
  if (script.cachedDataProduced) {
    this._cacheStore.set(filename, invalidationKey, script.cachedData);
  }
  var compiledWrapper = script.runInThisContext({ filename, lineOffset: 0, columnOffset: 0, displayErrors: true });
  ...
};

v8‑snapshot

Serialize the memory state after code execution and load it at startup, skipping both compilation and execution. It works well for third‑party libraries but not for code with side effects.

2. Reorder Main‑Process Workflow

Split the bundle and execute only the essential code first, deferring non‑critical work.

New timing shows the main‑process ready event occurs within ~90 ms after code start.

3. Accelerate Renderer Rendering

Use requestIdleCallback for low‑priority tasks.

Remove or refactor synchronous sendSync calls and heavy electron‑store usage.

Load only the CSS required for the first screen; use Tailwind’s purge feature.

Avoid modulepreload when it slows down the first paint.

CSS size directly impacts render performance.

4. Improve Perceived Speed

Show a skeleton screen (a lightweight C++ loading exe) while the renderer initializes, then hide it once the UI is ready.

Adding a single line of text to the DOM can noticeably reduce white‑screen time because the browser prioritises visible information.

Other Considerations

Upgrade Electron to benefit from upstream performance fixes.

Continuously monitor startup metrics; regressions should trigger re‑optimization.

Conclusion

The described optimizations reduced the perceived startup time of the cash‑register app on a low‑end machine from about 10 seconds to roughly 1 second. The most effective measure was workflow reordering and bundle splitting, while techniques like code‑cache and snapshots were only useful after careful measurement.

frontendOptimizationElectronstartupnode
Goodme Frontend Team
Written by

Goodme Frontend Team

Regularly sharing the team's insights and expertise in the frontend field

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.