Library / SDK
AlexTorresDev/custom-electron-titlebar avatar
AlexTorresDev/custom-electron-titlebar

custom-electron-titlebar puts a VS Code style title bar inside your Electron app

Custom electon title bar inpire on VS Code title bar

903 stars148 forksTypeScriptMIT

At a glance

What is it?
custom-electron-titlebar is a TypeScript library that gives an Electron window a themed title bar, menu bar and window controls, configured from a JSON theme file loaded in the main process. It suits desktop apps that already run on Electron and want a branded frame, and it is overkill for teams that only need the native window controls.
Who is it for?
Use custom-electron-titlebar when your Electron app needs a branded frame, a menu bar and theme colors that follow the system, and you are willing to depend on a wiki page for the constructor options. Skip it if you only want the native window buttons, since the Electron titleBarOverlay option covers that case with far less code.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library replaces in an Electron window

A plain Electron window has a frame that belongs to the operating system. On macOS the traffic light buttons sit in the native title bar, and on Windows the frame is drawn by the system with no room for your own menu bar. That is fine for a utility, but it is a problem for applications that want to look like a first party product.

custom-electron-titlebar addresses that by drawing the frame in the renderer process, the way an editor such as Visual Studio Code does. The project description says the bar is inspired by the VS Code title bar, and the project page shows four configurations: a standard title bar, a bottom menu bar, a menu, and a custom color scheme. The bar is not a screenshot. It is a set of components that you configure, including the color of elements, the icons and the menu position, with the full option list kept in the project wiki rather than in the installation notes.

The target audience is developers who already have an Electron app and want to change its frame without hand writing a drag region, a set of window buttons and a menu bar from scratch. The library is published to npm as custom-electron-titlebar and is written in TypeScript.

The main process owns the theme, the renderer draws it

The data flow is split across the two Electron processes, and this is the part worth understanding before you use it.

In the main process, setupTitlebarAndAttachToWindow takes the BrowserWindow and an options object. The usage notes describe that call as setting up main IPC, attaching window listeners and loading the theme config. The theme path is the option that matters, and it is a path to a JSON file on disk rather than a JavaScript object.

In the preload file, createTitlebarOnDOMContentLoaded is called with no arguments. The documentation states that the theme configuration is loaded automatically from the main process, so the renderer does not read the file and does not receive theme keys as arguments. Themes are loaded in the main process and delivered to the renderer over IPC.

That arrangement has a practical consequence. The title bar cannot be rendered before IPC delivers the theme, so there is a brief window at startup where the bar has no colors, and any code that reads theme values synchronously at page load will read the wrong thing. The renderer side of the library is responsible for that sequencing.

Installing and wiring up the first title bar

The package can be installed with npm, pnpm or yarn. All three forms are listed, and they do the same thing.

sh
npm install custom-electron-titlebar
sh
pnpm add custom-electron-titlebar
sh
yarn add custom-electron-titlebar

The window itself needs the frame hidden, which is what titleBarStyle does. This is the browser window configuration from the main process example, with the setup call that follows it:

js
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    //frame: false, // needed if process.versions.electron < 14
    titleBarStyle: 'hidden',
    /* You can use *titleBarOverlay: true* to use the original Windows controls */
    titleBarOverlay: true,
    webPreferences: {
      sandbox: false,
      preload: path.join(__dirname, 'preload.js')
    }
  });

The commented line about frame: false is the older Electron style, kept for versions below 14. The preload side is smaller, since the renderer needs no arguments.

js
import { createTitlebarOnDOMContentLoaded } from "custom-electron-titlebar";

// Theme configuration is automatically loaded from main process
createTitlebarOnDOMContentLoaded();

The example directory in the repository shows the whole arrangement, with main.js, preload.js, renderer.js, styles.css, index.html and a titlebar.theme.json sitting next to each other. The package scripts include an electron start command that points at example/main.js, and a launch script that builds the package first and then starts the example.

The theme file is a fixed version 1 schema

Colors are not passed in code. The theme is a JSON file, and the schema is pinned to version 1 with a fixed set of keys.

json
{
  "version": 1,
  "fontFamily": "Segoe UI, Arial, sans-serif",
  "fontSize": 13,
  "colors": {
    "titlebar": "#1f2430",
    "titlebarForeground": "#f3f4f6",
    "menuBar": "#181c25",
    "menuItemSelection": "#2f3a4f",
    "menuSeparator": "#4b5563",
    "svg": "#e5e7eb"
  }
}

Six color slots are defined: the bar itself, its foreground, the menu bar, the menu item selection, the menu separator and the SVG icon color. There is no dark and light mode switch in this schema, and no per platform override. If your app needs a light theme on macOS and a dark theme on Windows, this file does not express that, and the wiki would need to be checked for an option the install notes do not mention.

The font family and size are part of the same file, so typography travels with color. That keeps the frame consistent with a design system, but it also means the renderer cannot choose a different font without editing the theme.

Where the package.json export paths disagree

The published package.json is worth reading before you upgrade. The top level main field points at dist/index.js and the module field points at dist/index.mjs. The exports map, however, sends the root import to dist/dist/index.js and dist/dist/index.mjs, with a doubled dist segment, while the ./main subpath points at dist/main/index.js and dist/main/index.mjs with a single segment.

The types field on the exports map repeats the same doubled path. A bundler that honours the exports map rather than the legacy main field will therefore look for the artifact one directory deeper than the one the legacy fields describe. Whether that file exists depends on what tsup writes, and tsup.config.ts is the file that decides. The install notes do not explain the discrepancy, so treat it as something to confirm against the published tarball for the version you pin.

This is exactly the kind of detail that surfaces as a module not found error in a fresh install and nowhere else. If your build resolves the package today, record the resolved path and recheck it on each upgrade.

What the project does not tell you

The project page is short and delegates a lot to the wiki. It states that the constructor options and the available methods are documented there, and it does not list them inline. That leaves a real gap for anyone auditing a title bar before shipping it, because the option names that control the menu position and the icons are not visible from the installation notes alone.

Other gaps are worth naming. The example preload uses sandbox: false, which means the sandbox is off in the sample configuration, and nothing is said about what happens with a sandboxed renderer. The peer dependency requires electron greater than 36.0.0, so the library will not install cleanly next to an older Electron, and no fallback is described for that case.

The repository does contain tests, a jest configuration and a mock directory, and the CI workflow is referenced by a badge, but the project page documents neither how to run the test suite nor what the suite covers. The example build path is the practical way to see the library working.

Releases, versioning and the cost of an upgrade

The library is published under the MIT license, which is a permissive choice for a library you embed in a commercial desktop application.

The release history is uneven. Version 4.4.1 and 4.4.0 were published in March 2026, two days apart, while the version before them, 4.2.8, dates from January 2024. The last push to the repository was on 2026-10-15, so work is continuing on the main branch even though the published releases are not evenly spaced.

Two files in the repository root hint at how releases are produced. A .releaserc.yml file points at a release automation configuration, and a revert.sh script sits at the top level with a postversion script that copies package.json and runs that revert script. The upgrade cost for a user is therefore a normal semver bump, but the automated release process means you should read the changelog between the version you pin and the one you move to, especially across a gap as wide as the one between 4.2.8 and 4.4.0.

Editorial conclusion

Use custom-electron-titlebar when your Electron app needs a branded frame, a menu bar and theme colors that follow the system, and you are willing to depend on a wiki page for the constructor options. Skip it if you only want the native window buttons, since the Electron titleBarOverlay option covers that case with far less code. Before you commit, verify that the export paths in package.json resolve on your Electron version, that your Electron release satisfies the peer dependency of greater than 36.0.0, and that the theme you need fits the version 1 schema.

Frequently asked questions

How do I install custom-electron-titlebar in an Electron app?

The install notes give npm, pnpm and yarn commands, all of which install the same package: npm install custom-electron-titlebar, pnpm add custom-electron-titlebar, or yarn add custom-electron-titlebar.

How is the theme configuration loaded in custom-electron-titlebar?

You pass a themeConfigPath option to setupTitlebarAndAttachToWindow in the main process, pointing at a JSON file such as titlebar.theme.json. The theme is loaded in the main process and delivered to the renderer over IPC, so the preload script only needs to call createTitlebarOnDOMContentLoaded.

Which Electron versions does custom-electron-titlebar support?

The package declares a peer dependency on electron greater than 36.0.0. The install notes also keep a commented frame: false line for Electron versions below 14, but the declared peer dependency is the constraint that matters for an install.

Do I still need titleBarOverlay when using custom-electron-titlebar?

The example shows titleBarStyle: 'hidden' together with titleBarOverlay: true, and notes that the overlay option uses the original Windows controls. You can hide the frame without the overlay if you want the library to draw every control itself.

Official sources

  1. AlexTorresDev/custom-electron-titlebar on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/alextorresdev-custom-electron-titlebar.svg)](https://hysenlabs.com/projects/alextorresdev-custom-electron-titlebar)

Was this useful?