README.md

August 24, 2026 · View on GitHub

GoGPU Logo

systray

Pure Go system tray library for Windows, macOS, and Linux
Zero CGO. Cross-platform. Multiple trays. Context menus. Notifications.

CI Coverage Go Reference Go Report Card License Zero CGO


Features

  • Pure Go — zero CGO on all platforms. Single binary, easy cross-compilation
  • Multiple trays — create as many tray icons as you need
  • Context menus — nested menus with checkboxes, separators, icons, and submenus
  • Notifications — balloon tips (Windows), notification center (macOS), D-Bus notifications (Linux)
  • Dark mode — automatic icon switching for light/dark themes (Windows)
  • Template icons — macOS-native monochrome icons that adapt to system theme
  • Builder pattern — fluent API for clean, readable code
  • Message loop — built-in Run() blocks and pumps the platform event loop
  • Standalone — no dependency on gogpu framework. Use in any Go application

Platform Implementation

PlatformAPIDependencyStatus
WindowsShell_NotifyIconW (shell32.dll)golang.org/x/sys/windowsImplemented
macOSNSStatusBar / NSStatusItem (AppKit)github.com/go-webgpu/goffiImplemented
LinuxStatusNotifierItem (D-Bus SNI)github.com/godbus/dbus/v5Implemented

All platform implementations use Pure Go FFI — no C compiler required.

Installation

go get github.com/gogpu/systray

Requirements: Go 1.25+

Quick Start

package main

import (
    "fmt"
    "os"

    "github.com/gogpu/systray"
)

func main() {
    tray := systray.New()

    // Build context menu
    menu := systray.NewMenu()
    menu.Add("Hello", func() { fmt.Println("Hello clicked!") })
    menu.Add("Show Notification", func() {
        tray.ShowNotification("My App", "Hello from systray!")
    })
    menu.AddSeparator()
    menu.AddCheckbox("Check me", false, func() { fmt.Println("Toggled") })
    menu.AddSeparator()
    menu.Add("Quit", func() {
        tray.Remove()
        os.Exit(0)
    })

    // Configure and show
    tray.SetIcon(iconPNG).
        SetTooltip("My Application").
        SetMenu(menu)
    tray.OnClick(func() { fmt.Println("Left click!") })
    tray.Show()

    // Run the platform message loop (blocks until Quit)
    if err := tray.Run(); err != nil {
        fmt.Println("error:", err)
    }
}

API Reference

SystemTray

// Create and lifecycle
tray := systray.New()              // Create a new system tray icon
tray.Show()                        // Show tray icon
tray.Hide()                        // Hide tray icon (without removing)
tray.Run()                         // Block and pump the platform message loop
tray.Remove()                      // Destroy tray icon and release resources

// Icon management
tray.SetIcon(png []byte)           // Set tray icon (PNG format)
tray.SetDarkModeIcon(png []byte)   // Auto-switch in dark mode (Windows)
tray.SetTemplateIcon(png []byte)   // macOS template image (monochrome)

// Text and menu
tray.SetTooltip(text string)       // Hover tooltip
tray.SetMenu(menu *Menu)           // Attach context menu (nil removes it)

// Events
tray.OnClick(fn func())            // Left click handler
tray.OnDoubleClick(fn func())      // Double click handler
tray.OnRightClick(fn func())       // Right click handler

// Notifications
tray.ShowNotification(title, message string)  // OS-level notification

// Position (Windows only; macOS/Linux return zeros)
x, y, w, h := tray.Bounds()       // Tray icon screen position

All setter methods return *SystemTray for fluent chaining:

tray.SetIcon(icon).SetTooltip("Ready").SetMenu(menu).Show()
menu := systray.NewMenu()

item := menu.Add("Label", onClick)                   // Normal item → *MenuItem
check := menu.AddCheckbox("Toggle", checked, onChange) // Checkbox → *MenuItem
menu.AddSeparator()                                    // Separator → *Menu (chaining)
sub := menu.AddSubmenu("More", submenu)                // Submenu → *MenuItem
icon := menu.AddWithIcon("Save", iconPNG, onClick)     // With icon → *MenuItem

Add, AddCheckbox, AddSubmenu, AddWithIcon return *MenuItem for dynamic updates. AddSeparator returns *Menu for chaining.

Dynamic Menu Updates

Update menu items at runtime from any goroutine — thread-safe, changes are applied in-place via native platform APIs (no menu rebuild). On macOS, updates are automatically dispatched to the main thread:

item.SetLabel("New Label")     // Change display text
check.SetChecked(false)        // Change checked state
sub.SetDisabled(true)          // Disable/enable
icon.SetIcon(newIconPNG)       // Change icon

// Thread-safe getters
if check.IsChecked() { ... }   // Read current state
if sub.IsDisabled() { ... }

Multiple Trays

// Each tray is independent with its own icon, menu, and handlers
mainTray := systray.New().SetIcon(appIcon).SetMenu(mainMenu).Show()
statusTray := systray.New().SetIcon(statusIcon).SetTooltip("Status: OK").Show()

Dark Mode

systray supports automatic icon switching based on the system theme.

Windows — Use SetDarkModeIcon() to provide an alternative icon for dark mode. The library detects theme changes via WM_SETTINGCHANGE with "ImmersiveColorSet" and switches icons automatically:

tray.SetIcon(lightIcon).SetDarkModeIcon(darkIcon)

macOS — Use SetTemplateIcon() with a monochrome PNG. macOS renders template images with the correct color for the current menu bar appearance (light or dark). Only the alpha channel matters:

tray.SetTemplateIcon(monochromeIcon)

Linux — The SNI protocol delivers the icon pixmap to the desktop environment, which handles theme adaptation. No special API is needed.

Notifications

ShowNotification sends an OS-level notification from the tray icon:

tray.ShowNotification("Update Available", "Version 2.0 is ready to install.")
PlatformMechanismNotes
WindowsBalloon tip (Shell_NotifyIconW + NIF_INFO)Appears near the tray icon
macOSNSUserNotification / Notification CenterRequires notification permission on macOS 13+
Linuxorg.freedesktop.Notifications D-BusWorks on GNOME, KDE, XFCE, and other FreeDesktop-compliant DEs

Icon Guidelines

PlatformRecommended SizeFormatNotes
Windows16x16, 32x32PNGProvide both sizes for standard and HiDPI
macOS22x22, 44x44 (@2x)PNGMust be monochrome (template) for proper theme adaptation
Linux22x22, 24x24PNGSNI spec recommends 22x22

Input format: PNG bytes ([]byte). The library handles conversion to native format (HICON, NSImage, ARGB pixmap) internally.

For macOS, use SetTemplateIcon() with a monochrome PNG (only alpha channel matters). The system automatically adjusts the icon color for light/dark menu bar.

Architecture

systray.New()  ->  SystemTray (public API)
                       |
                  PlatformTray (internal interface)
                       |
          +------------+------------+
          |            |            |
     Win32 impl   macOS impl   Linux impl
     Shell_Notify  NSStatusBar   D-Bus SNI
     IconW         NSStatusItem  StatusNotifierItem

Follows the Qt6 QPlatformSystemTrayIcon three-layer pattern. Each platform implementation is isolated in its own file with build constraints.

Usage with gogpu

While systray is fully standalone, it integrates with the gogpu application framework. systray runs its own message loop via Run(), or you can manage the lifecycle manually alongside gogpu's event loop:

import (
    "github.com/gogpu/systray"
)

// Create tray icon alongside your gogpu app
tray := systray.New()
tray.SetIcon(icon).SetMenu(menu).Show()

// Show/hide window on tray click
tray.OnClick(func() {
    fmt.Println("Tray clicked — toggle window")
})

Comparison with Alternatives

Featuregogpu/systraygetlantern/systrayfyne-io/systray
Pure Go (zero CGO)YesNo (CGO on macOS/Linux)No (CGO on macOS/Linux)
Multiple traysYesNo (single global)No (single global)
Dynamic menu updatesYes (SetLabel/SetChecked/SetDisabled)Yes (SetTitle/Check/Uncheck)Yes (SetTitle/Check/Uncheck)
Dark mode auto-switchYes (Windows)NoNo
Template icons (macOS)YesYesYes
Nested menusYesYesYes
Menu item iconsYesYesYes
NotificationsYesNoNo
Builder patternYesNoNo
Built-in message loopYesYesYes
Wayland supportYes (D-Bus SNI)Yes (D-Bus SNI)Yes (D-Bus SNI)

Contributing

We welcome contributions! See CONTRIBUTING.md for guidelines.

Part of the GoGPU Ecosystem

systray is part of the GoGPU ecosystem — 1.2M+ lines of Pure Go, zero CGO. A GPU computing platform with a WebGPU implementation, shader compiler, 2D graphics library, and GUI toolkit.

LibraryPurpose
gogpuApplication framework, windowing
wgpuPure Go WebGPU (Vulkan/Metal/DX12/GLES)
nagaShader compiler (WGSL to SPIR-V/MSL/GLSL/HLSL/DXIL)
gg2D graphics with GPU acceleration
uiGUI toolkit (27 widgets, 4 themes)
systraySystem tray (this library)

Star History

Star History Chart

License

MIT License — see LICENSE for details.