Creating an ElectronJS Menubar App with a Tray Icon
Jason Gilmore•
This tutorial will show you how to add a custom icon to your Electron-powered menubar app.
Set Up the Main Process
First, create a Tray instance in your main process:
const { app, Tray, nativeImage } = require('electron')
const path = require('path')
let tray = null
app.whenReady().then(() => {
createTray()
})
function createTray() {
// Create the tray with an empty icon initially
tray = new Tray(nativeImage.createEmpty())
// Set basic properties
tray.setTitle('My App')
tray.setToolTip('My App')
}
Create the Icon
Your tray icon should be:
- PNG format
- Small size (recommended 16x16 or 32x32 pixels)
- Preferably with transparency
- Visible against both light and dark menubar backgrounds
function createTray() {
// Create the tray with an empty icon initially
tray = new Tray(nativeImage.createEmpty())
// Load and set the icon
const iconPath = path.join(__dirname, 'assets/icon.png')
if (fs.existsSync(iconPath)) {
const icon = nativeImage.createFromPath(iconPath)
const resizedIcon = icon.resize({ width: 24, height: 24 })
tray.setImage(resizedIcon)
}
}
Handle Dark/Light Mode
To support both dark and light modes, use the nativeTheme
API:
const { nativeTheme } = require('electron')
function createTray() {
// ... previous tray setup code ...
// Listen for theme changes
nativeTheme.on('updated', () => {
const iconPath = nativeTheme.shouldUseDarkColors
? path.join(__dirname, 'assets/icon-dark.png')
: path.join(__dirname, 'assets/icon-light.png')
if (fs.existsSync(iconPath)) {
const icon = nativeImage.createFromPath(iconPath)
const resizedIcon = icon.resize({ width: 24, height: 24 })
tray.setImage(resizedIcon)
}
})
}
Hide Dock Icon
Since this is a menubar app, you probably want to hide the dock icon otherwise it will consistently take up valuable space:
if (process.platform === 'darwin') {
app.dock.hide()
}
Add Click Handlers
Make your tray icon interactive by adding click handlers:
function createTray() {
// ... previous tray setup code ...
tray.on('click', (event) => {
// Handle left click
})
tray.on('right-click', (event) => {
// Handle right click, perhaps show a context menu
})
}
Complete Example
Here's a complete example with all of the above concepts incorporated:
const { app, Tray, nativeImage, nativeTheme } = require('electron')
const path = require('path')
const fs = require('fs')
let tray = null
app.whenReady().then(() => {
// Hide dock icon for menubar apps
if (process.platform === 'darwin') {
app.dock.hide()
}
createTray()
})
function createTray() {
// Create with empty icon initially
tray = new Tray(nativeImage.createEmpty())
// Set basic properties
tray.setTitle('')
tray.setToolTip('My App')
// Load initial icon
updateTrayIcon()
// Listen for theme changes
nativeTheme.on('updated', () => {
updateTrayIcon()
})
// Handle clicks
tray.on('click', (event) => {
// Handle left click
})
}
function updateTrayIcon() {
const iconPath = nativeTheme.shouldUseDarkColors
? path.join(__dirname, 'assets/icon-dark.png')
: path.join(__dirname, 'assets/icon-light.png')
if (fs.existsSync(iconPath)) {
const icon = nativeImage.createFromPath(iconPath)
const resizedIcon = icon.resize({ width: 24, height: 24 })
tray.setImage(resizedIcon)
}
}