Events Guide

NOTE: This guide is a work in progress

Events Guide

Events are the heartbeat of communication in Wails applications. They allow different parts of your application to talk to each other without being tightly coupled. This guide will walk you through everything you need to know about using events effectively in your Wails application.

Understanding Wails Events

Think of events as messages that get broadcast throughout your application. Any part of your application can listen for these messages and react accordingly. This is particularly useful for:

  • Responding to window changes: Know when your window is minimized, maximized, or moved
  • Handling system events: React to theme changes or power events
  • Custom application logic: Create your own events for features like data updates or user actions
  • Cross-component communication: Let different parts of your app communicate without direct dependencies

Event Naming Convention

All Wails events follow a namespace pattern to clearly indicate their origin:

  • common: - Cross-platform events that work on Windows, macOS, and Linux
  • windows: - Windows-specific events
  • mac: - macOS-specific events
  • linux: - Linux-specific events

For example:

  • common:WindowFocus - Window gained focus (works everywhere)
  • windows:APMSuspend - System is suspending (Windows only)
  • mac:ApplicationDidBecomeActive - App became active (macOS only)

Getting Started with Events

Listening to Events (Frontend)

The most common use case is listening for events in your frontend code:

import { Events } from '@wailsio/runtime';

// Listen for when the window gains focus
Events.On('common:WindowFocus', () => {
    console.log('Window is now focused!');
    // Maybe refresh some data or resume animations
});

// Listen for theme changes
Events.On('common:ThemeChanged', (event) => {
    console.log('Theme changed:', event.data);
    // Update your app's theme accordingly
});

// Listen for custom events from your Go backend
Events.On('my-app:data-updated', (event) => {
    console.log('Data updated:', event.data);
    // Update your UI with the new data
});

Emitting Events (Backend)

From your Go code, you can emit events that your frontend can listen to:

package main

import (
    "github.com/wailsapp/wails/v3/pkg/application"
    "time"
)

func (s *Service) UpdateData() {
    // Do some data processing...

    // Notify the frontend
    app := application.Get()
    app.Event.Emit("my-app:data-updated",
        map[string]interface{}{
            "timestamp": time.Now(),
            "count": 42,
        },
	)
}

Emitting Events (Frontend)

While not as commonly used, you can also emit events from your frontend that your Go code can listen to:

import { Events } from '@wailsio/runtime';

// Event without data
Events.Emit('myapp:close-window')

// Event with data
Events.Emit('myapp:disconnect-requested', 'id-123')

If you are using TypeScript in your frontend and registering typed events in your Go code, you will get event name autocomplete/checking and data type checking.

Removing Event Listeners

Always clean up your event listeners when they’re no longer needed:

import { Events } from '@wailsio/runtime';

// Store the handler reference
const focusHandler = () => {
    console.log('Window focused');
};

// Add the listener — capture the unsubscribe function it returns
const unsubscribe = Events.On('common:WindowFocus', focusHandler);

// Later, remove this specific listener via the returned unsubscribe
unsubscribe();

// Or remove ALL listeners for one (or more) event names — Events.Off takes only event-name strings
Events.Off('common:WindowFocus');
// Events.Off('common:WindowFocus', 'common:WindowLostFocus'); // variadic
// Events.OffAll(); // remove every listener for every event (no args)

Common Use Cases

1. Pause/Resume on Window Focus

Many applications need to pause certain activities when the window loses focus:

import { Events } from '@wailsio/runtime';

let animationRunning = true;

Events.On('common:WindowLostFocus', () => {
    animationRunning = false;
    pauseBackgroundTasks();
});

Events.On('common:WindowFocus', () => {
    animationRunning = true;
    resumeBackgroundTasks();
});

2. Responding to Theme Changes

Keep your app in sync with the system theme:

import { Events } from '@wailsio/runtime';

Events.On('common:ThemeChanged', (event) => {
    const isDarkMode = event.data.isDarkMode;

    if (isDarkMode) {
        document.body.classList.add('dark-theme');
        document.body.classList.remove('light-theme');
    } else {
        document.body.classList.add('light-theme');
        document.body.classList.remove('dark-theme');
    }
});

3. Handling File Drops

Make your app accept dragged files:

import { Events } from '@wailsio/runtime';

Events.On('common:WindowFilesDropped', (event) => {
    const files = event.data.files;

    files.forEach(file => {
        console.log('File dropped:', file);
        // Process the dropped files
        handleFileUpload(file);
    });
});

4. Window Lifecycle Management

Respond to window state changes:

import { Events } from '@wailsio/runtime';

Events.On('common:WindowClosing', () => {
    // Save user data before closing
    saveApplicationState();

    // You could also prevent closing by returning false
    // from a registered window close handler
});

Events.On('common:WindowMaximise', () => {
    // Adjust UI for maximized view
    adjustLayoutForMaximized();
});

Events.On('common:WindowRestore', () => {
    // Return UI to normal state
    adjustLayoutForNormal();
});

5. Platform-Specific Features

Handle platform-specific events when needed:

import { Events } from '@wailsio/runtime';

// Windows-specific power management
Events.On('windows:APMSuspend', () => {
    console.log('System is going to sleep');
    saveState();
});

Events.On('windows:APMResumeSuspend', () => {
    console.log('System woke up');
    refreshData();
});

// macOS-specific app lifecycle
Events.On('mac:ApplicationWillTerminate', () => {
    console.log('App is about to quit');
    performCleanup();
});

Creating Custom Events

You can create your own events for application-specific needs.

Backend (Go)

// Emit a custom event when data changes

func (s *Service) ProcessUserData(userData UserData) error {
    // Process the data...

    app := application.Get()
    // Notify all listeners
    app.Event.Emit("user:data-processed",
        map[string]interface{}{
            "userId": userData.ID,
            "status": "completed",
            "timestamp": time.Now(),
        },
    )
    return nil
}

// Emit periodic updates
func (s *Service) StartMonitoring() {
    app := application.Get()
    ticker := time.NewTicker(5 * time.Second)
    go func() {
        for range ticker.C {
            stats := s.collectStats()
            app.Event.Emit("monitor:stats-updated", stats)
        }
    }()
}

Frontend (JavaScript)

import { Events } from '@wailsio/runtime';

// Listen for your custom events
Events.On('user:data-processed', (event) => {
    const { userId, status, timestamp } = event.data;

    showNotification(`User ${userId} processing ${status}`);
    updateUIWithNewData();
});

Events.On('monitor:stats-updated', (event) => {
    updateDashboard(event.data);
});

Typed Events with Type Safety

Wails v3 supports typed events with full TypeScript type safety through event registration and automatic binding generation.

Registering Custom Events

Call application.RegisterEvent at init time to register custom event names with their data types:

package main

import "github.com/wailsapp/wails/v3/pkg/application"

type UserLoginData struct {
    UserID   string
    Username string
    LoginTime string
}

type MonitorStats struct {
    CPUUsage    float64
    MemoryUsage float64
}

func init() {
    // Register events with their data types
    application.RegisterEvent[UserLoginData]("user:login")
    application.RegisterEvent[MonitorStats]("monitor:stats")

    // Register events without data (void events)
    application.RegisterEvent[application.Void]("app:ready")
}

Benefits of Event Registration

Once registered, data arguments passed to Event.Emit are type-checked against the specified type. On mismatch:

  • An error is emitted and logged (or passed to the registered error handler)
  • The offending event will not be propagated
  • This ensures the data field of registered events is always assignable to the declared type

Strict Mode

Use the strictevents build tag to enable warnings for unregistered events in development:

bash
go build -tags strictevents

With strict mode enabled, the runtime emits at most one warning per unregistered event name to avoid spamming logs.

TypeScript Binding Generation

The binding generator outputs TypeScript definitions and glue code for transparent typed event support in the frontend.

1. Set up the Vite Plugin

In your vite.config.ts:

import { defineConfig } from 'vite'
import wails from '@wailsio/runtime/plugins/vite'

export default defineConfig({
  plugins: [wails()],
})

2. Generate Bindings

Run the binding generator:

bash
wails3 generate bindings

This creates TypeScript files in your frontend directory with typed event creators and data interfaces.

3. Use Typed Events in Frontend

import { Events } from '@wailsio/runtime'
import { UserLogin, MonitorStats } from './bindings/events'

// Type-safe event emission with autocomplete
Events.Emit(UserLogin({
    UserID: "123",
    Username: "john_doe",
    LoginTime: new Date().toISOString()
}))

// Type-safe event listening
Events.On(UserLogin, (event) => {
    // event.data is typed as UserLoginData
    console.log(`User ${event.data.Username} logged in`)
})

Events.On(MonitorStats, (event) => {
    // event.data is typed as MonitorStats
    updateDashboard({
        cpu: event.data.CPUUsage,
        memory: event.data.MemoryUsage
    })
})

The typed events provide:

  • Autocomplete for event names
  • Type checking for event data
  • Compile-time errors for mismatched data types
  • IntelliSense documentation

Event Reference

Common Events (Cross-platform)

These events work on all platforms:

Event Description When to Use
common:ApplicationStarted Application has fully started Initialize your app, load saved state
common:WindowRuntimeReady Wails runtime is ready Start making Wails API calls
common:ThemeChanged System theme changed Update app appearance
common:SystemWillSleep System is about to suspend Flush state, close sockets
common:SystemDidWake System resumed from suspend Reconnect, refresh stale data
common:WindowFocus Window gained focus Resume activities, refresh data
common:WindowLostFocus Window lost focus Pause activities, save state
common:WindowMinimise Window was minimized Pause rendering, reduce resource usage
common:WindowMaximise Window was maximized Adjust layout for full screen
common:WindowRestore Window restored from min/max Return to normal layout
common:WindowClosing Window is about to close Save data, cleanup resources
common:WindowFilesDropped Files dropped on window Handle file imports
common:WindowDidResize Window was resized Adjust layout, rerender charts
common:WindowDidMove Window was moved Update position-dependent features

Platform-Specific Events

Windows Events

Key events for Windows applications:

Event Description Use Case
windows:SystemThemeChanged Windows theme changed Update app colors
windows:APMSuspend System suspending Save state, pause operations
windows:APMResumeAutomatic System resumed (always fires on resume) Restore state, refresh data
windows:APMResumeSuspend System resumed via user input (after APMResumeAutomatic) Distinguish user-initiated wake
windows:APMPowerStatusChange Power status changed Adjust performance settings

macOS Events

Important macOS application events:

Event Description Use Case
mac:ApplicationDidBecomeActive App became active Resume operations
mac:ApplicationDidResignActive App became inactive Pause operations
mac:ApplicationWillTerminate App will quit Final cleanup
mac:ApplicationWillSleep System about to suspend Save state, close sockets
mac:ApplicationDidWake System resumed Reconnect, refresh
mac:ApplicationScreensDidSleep Displays went to sleep Pause rendering (distinct from system sleep)
mac:ApplicationScreensDidWake Displays woke Resume rendering
mac:WindowDidEnterFullScreen Entered fullscreen Adjust UI for fullscreen
mac:WindowDidExitFullScreen Exited fullscreen Restore normal UI

Linux Events

Core Linux window events:

Event Description Use Case
linux:SystemThemeChanged Desktop theme changed Update app theme
linux:SystemWillSleep System about to suspend (logind) Save state
linux:SystemDidWake System resumed (logind) Reconnect, refresh
linux:WindowFocusIn Window gained focus Resume activities
linux:WindowFocusOut Window lost focus Pause activities
linux:WindowLoadStarted WebView started loading Show loading indicator
linux:WindowLoadRedirected WebView redirected Track navigation redirects
linux:WindowLoadCommitted WebView committed load Content is being received
linux:WindowLoadFinished WebView finished loading Hide loading indicator, inject JS/CSS

Best Practices

1. Use Event Namespaces

When creating custom events, use namespaces to avoid conflicts:

import { Events } from '@wailsio/runtime';

// Good - namespaced events
Events.Emit('myapp:user:login');
Events.Emit('myapp:data:updated');
Events.Emit('myapp:network:connected');

// Avoid - generic names that might conflict
Events.Emit('login');
Events.Emit('update');

2. Clean Up Listeners

Always remove event listeners when components unmount:

import { Events } from '@wailsio/runtime';

// React example
useEffect(() => {
    const handler = (event) => {
        // Handle event
    };

    const off = Events.On('common:WindowDidResize', handler);

    // Cleanup — call the unsubscribe returned by Events.On
    return () => {
        off();
    };
}, []);

3. Handle Platform Differences

Check platform availability when using platform-specific events:

import { Events } from '@wailsio/runtime';

// Platform-specific events can be registered unconditionally;
// they will simply never fire on unsupported platforms.
Events.On('windows:APMSuspend', handleSuspend);
Events.On('mac:ApplicationWillTerminate', handleTerminate);

4. Don’t Overuse Events

While events are powerful, don’t use them for everything:

  • ✅ Use events for: System notifications, lifecycle changes, broadcast updates
  • ❌ Avoid events for: Direct function returns, single component updates, synchronous operations

Debugging Events

To debug event issues:

import { Events } from '@wailsio/runtime';

// Log all events (development only)
if (isDevelopment) {
    const originalOn = Events.On;
    Events.On = function(eventName, handler) {
        console.log(`[Event Registered] ${eventName}`);
        return originalOn.call(this, eventName, function(event) {
            console.log(`[Event Fired] ${eventName}`, event);
            return handler(event);
        });
    };
}

Source of Truth

The complete list of available events can be found in the Wails source code:

Always refer to these files for the most up-to-date event names and availability.

Summary

Events in Wails provide a powerful, decoupled way to handle communication in your application. By following the patterns and practices in this guide, you can build responsive, platform-aware applications that react smoothly to system changes and user interactions.

Remember: start with common events for cross-platform compatibility, add platform-specific events when needed, and always clean up your event listeners to prevent memory leaks.

Edit page

Last updated: