Menu, Tray, dan Notification itu tiga sentuhan kecil yang bikin aplikasi Electron kamu berasa kayak aplikasi desktop beneran, bukan cuma halaman web yang dijejalin ke dalam jendela. Coba deh bandingin: aplikasi tanpa menu bar itu berasa kosong, sedangkan aplikasi yang menunya rapi, ada ikon di system tray, plus notifikasi yang nongol pas waktunya — langsung kelihatan niat dan profesional. Nah, ketiganya bisa kamu bikin pakai API bawaan Electron, dan semuanya hidup di main process. Jadi kalau kamu ngerasa perlu balik dulu ke konsep dasar soal pemisahan proses, tengok lagi mengenal Electron.
Menu aplikasi (bikin menu bar dari template)
Menu bar itu deretan menu yang nangkring di bagian atas aplikasi — File, Edit, View, dan seterusnya. Di Electron kamu nggak nulis HTML buat ini. Yang kamu lakuin cuma mendeskripsikan struktur menunya sebagai array objek, terus Electron yang ngubahnya jadi menu native sesuai sistem operasi.
Caranya: pakai Menu.buildFromTemplate buat ngebangun menu dari template, terus Menu.setApplicationMenu buat masangnya. Lakuin ini setelah aplikasi siap, biasanya di dalam app.whenReady().
const { app, Menu, BrowserWindow } = require('electron')
function buatMenu() {
const template = [
{
label: 'Berkas',
submenu: [
{
label: 'Buka',
accelerator: 'CmdOrCtrl+O',
click: () => console.log('Buka ditekan'),
},
{ type: 'separator' },
{ role: 'quit', label: 'Keluar' },
],
},
{
label: 'Ubah',
submenu: [
{ role: 'undo', label: 'Urungkan' },
{ role: 'redo', label: 'Ulangi' },
{ type: 'separator' },
{ role: 'copy', label: 'Salin' },
{ role: 'paste', label: 'Tempel' },
],
},
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
app.whenReady().then(() => {
buatMenu()
})
Ada dua hal yang perlu kamu perhatiin di sini. Yang pertama, accelerator itu pintasan keyboard. Kalau kamu tulis CmdOrCtrl+O, dia otomatis jadi Cmd di macOS dan Ctrl di Windows/Linux — nggak perlu bikin dua versi. Yang kedua, role itu peran bawaan. Begitu kamu nulis role: 'copy', Electron udah nyiapin perilaku salin standar tanpa kamu perlu nulis kode apa pun. Jelas jauh lebih hemat tenaga daripada nanganin semuanya sendiri.
Kalau ada menu yang butuh logika sendiri, tinggal isi properti click dengan fungsi. Fungsi itu jalan di main process, jadi kamu punya akses penuh ke modul Node dan API Electron.
Context menu (klik kanan)
Context menu itu menu yang nongol pas kamu klik kanan. Ada dua cara umum buat bikinnya, dan dua-duanya sah dipakai — tinggal sesuain sama kebutuhan kamu.
Cara pertama, dengerin event context-menu di webContents jendela. Event ini kepicu tiap kali user klik kanan di dalam jendela. Nah di situ kamu bangun menunya, terus panggil popup() buat nampilinnya.
const { Menu } = require('electron')
function pasangMenuKonteks(win) {
win.webContents.on('context-menu', (event, params) => {
const menu = Menu.buildFromTemplate([
{
label: 'Salin teks',
enabled: params.selectionText.length > 0,
click: () => win.webContents.copy(),
},
{ type: 'separator' },
{ label: 'Muat ulang', click: () => win.webContents.reload() },
])
menu.popup({ window: win })
})
}
Objek params di sini kepakai banget. Dia ngasih tahu konteks kliknya: ada teks yang keseleksi atau nggak (params.selectionText), kliknya kena gambar atau link, dan sebagainya. Info ini bisa kamu manfaatin buat cuma nampilin item yang relevan aja. Contoh di atas, misalnya, cuma ngaktifin "Salin teks" kalau emang ada teks yang lagi diseleksi.
Cara kedua, panggil menu.popup() langsung kapan aja dari main process, misalnya sebagai respons atas pesan yang datang dari UI. Pola yang ini bakal kita bahas di bagian IPC nanti.
Ikon Tray (naruh app di system tray + menu)
System tray itu area ikon-ikon kecil di sudut layar — deket jam kalau di Windows, atau di menu bar atas kalau di macOS. Naruh ikon di sana tuh berguna banget buat aplikasi yang jalan di background, kayak pemutar musik atau alat monitoring.
Bikin objek Tray dengan path ke file ikonnya. Habis itu pasang tooltip dan context menu yang nongol pas ikonnya diklik kanan. Nah, ada satu jebakan yang sering bikin pemula bingung: simpan variabel tray di scope yang awet — di luar fungsi, atau di level modul. Kalau nggak, garbage collector bisa ngehapus dia dan ikonnya mendadak ilang gitu aja.
const { app, Tray, Menu, nativeImage } = require('electron')
const path = require('node:path')
let tray = null
function buatTray() {
const ikon = nativeImage.createFromPath(
path.join(__dirname, 'assets', 'tray.png')
)
tray = new Tray(ikon)
const menu = Menu.buildFromTemplate([
{ label: 'Tampilkan', click: () => console.log('Tampilkan jendela') },
{ type: 'separator' },
{ label: 'Keluar', click: () => app.quit() },
])
tray.setToolTip('Aplikasi Electron-ku')
tray.setContextMenu(menu)
}
app.whenReady().then(buatTray)
Beberapa catatan kecil. Pakai nativeImage.createFromPath biar ikonnya kemuat dengan benar di semua platform — walau sebenernya kamu juga boleh kasih path string langsung ke new Tray(). Ukuran ikon yang aman itu sekitar 16x16 atau 32x32 piksel, format PNG. Khusus di macOS, kamu bisa pakai gambar template (hitam-transparan) biar ikonnya otomatis nyesuain sama tema terang atau gelap.
Selain lewat menu, kamu juga bisa nangkep klik biasa di ikonnya pakai tray.on('click', ...). Pola yang umum: klik kiri buat nampilin jendela, klik kanan buat buka context menu.
Notifikasi native
Notifikasi itu pesan pop-up yang dimunculin sama sistem operasi. Di Electron, kamu bikinnya pakai kelas Notification. Bikin objeknya lengkap dengan judul dan isi, terus tinggal panggil show(). Segampang itu.
const { Notification } = require('electron')
function kirimNotifikasi() {
if (!Notification.isSupported()) return
const notif = new Notification({
title: 'Unduhan selesai',
body: 'Berkas laporan.pdf berhasil disimpan.',
})
notif.on('click', () => {
console.log('Notifikasi diklik')
})
notif.show()
}
Biasakan selalu ngecek Notification.isSupported() dulu, soalnya nggak semua lingkungan mendukung notifikasi. Kamu juga bisa dengerin event click buat ngerespons pas user mencet notifikasinya — misalnya buka jendela aplikasi atau halaman tertentu.
Ada satu hal khusus buat Windows: biar notifikasinya tampil dengan nama dan ikon aplikasi kamu yang bener, set App User Model ID lewat app.setAppUserModelId('com.namamu.appku') — cukup sekali aja pas aplikasi mulai. Tanpa itu, notifikasi kamu bisa muncul dengan nama generik yang nggak jelas. Di macOS dan Linux, urusan ini umumnya udah jalan otomatis.
Nyambungin ke UI lewat IPC (contoh singkat)
Semua kode yang tadi kita tulis jalan di main process. Tapi seringnya, yang jadi pemicu justru datang dari antarmuka alias renderer. Contoh gampangnya: user mencet tombol di halaman, terus kamu pengen munculin notifikasi. Masalahnya, renderer nggak punya akses langsung ke Notification punya main. Jadi kamu butuh jembatan, dan jembatan itu namanya IPC.
Polanya simpel: renderer ngirim pesan, main yang dengerin dan ngeksekusi. Di preload, kamu ekspos fungsi yang aman lewat contextBridge. Di main, kamu dengerin pakai ipcMain.on.
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
beriTahu: (judul, isi) => ipcRenderer.send('tampilkan-notif', judul, isi),
})
// main.js
const { ipcMain, Notification } = require('electron')
ipcMain.on('tampilkan-notif', (event, judul, isi) => {
if (!Notification.isSupported()) return
new Notification({ title: judul, body: isi }).show()
})
Dari sisi halaman, manggilnya jadi sesederhana window.api.beriTahu('Halo', 'Pesan dari UI'). Pola yang sama juga berlaku kalau kamu mau memicu context menu atau ngeupdate menu tray dari renderer. Intinya selalu sama: kerjaan native dilakuin di main, dan UI cukup ngirim sinyal doang. Kalau bagian ini kerasa masih asing buat kamu, mending baca dulu pembahasan lengkapnya di IPC di Electron, soalnya di sana semua konsep komunikasi antar-proses dikupas langkah demi langkah.
Penutup
Sekarang kamu udah pegang tiga elemen yang bikin aplikasi Electron kerasa native: menu aplikasi yang dibangun dari template, context menu buat klik kanan, ikon di system tray lengkap dengan menunya, plus notifikasi yang muncul langsung dari sistem. Kuncinya cuma satu: semua API ini hidup di main process, dan kalau perlu dipicu dari antarmuka, tinggal kamu lewatin aja lewat IPC. Polanya konsisten dan gampang dirawat begitu kamu udah kebiasa.
Saranku, mulai aja dari yang kecil. Tambahin menu bar sederhana ke proyek kamu hari ini, terus pasang satu notifikasi. Sedikit demi sedikit, aplikasi kamu bakal kerasa makin matang. Buat ngeliat peta perjalanan kita secara utuh, balik lagi ke seri Belajar Electron. Di bab berikutnya kita bakal masuk ke topik yang super praktis: ngakses file dan nampilin dialog bawaan sistem, biar aplikasi kamu bisa buka, simpan, dan milih berkas layaknya aplikasi desktop pada umumnya.