Message dialogs

Message dialogs

Wails provides native message dialogs with platform-appropriate appearance: info, warning, error, and question dialogs with customisable titles, messages, and buttons. Simple API, native behaviour, accessible by default.

Creating Dialogs

Message dialogs are accessed through the app.Dialog manager:

app.Dialog.Info()
app.Dialog.Question()
app.Dialog.Warning()
app.Dialog.Error()

All methods return a *MessageDialog that can be configured using method chaining.

Information dialog

Display informational messages:

app.Dialog.Info().
    SetTitle("Success").
    SetMessage("File saved successfully!").
    Show()

Use cases:

  • Success confirmations
  • Completion notices
  • Informational messages
  • Status updates

Example - Save confirmation:

func saveFile(app *application.App, path string, data []byte) error {
    if err := os.WriteFile(path, data, 0644); err != nil {
        return err
    }

    app.Dialog.Info().
        SetTitle("File Saved").
        SetMessage(fmt.Sprintf("Saved to %s", filepath.Base(path))).
        Show()

    return nil
}

Warning dialog

Show warnings:

app.Dialog.Warning().
    SetTitle("Warning").
    SetMessage("This action cannot be undone.").
    Show()

Use cases:

  • Non-critical warnings
  • Deprecation notices
  • Caution messages
  • Potential issues

Example - Disk space warning:

func checkDiskSpace(app *application.App) {
    available := getDiskSpace()

    if available < 100*1024*1024 { // Less than 100MB
        app.Dialog.Warning().
            SetTitle("Low Disk Space").
            SetMessage(fmt.Sprintf("Only %d MB available.", available/(1024*1024))).
            Show()
    }
}

Error dialog

Display errors:

app.Dialog.Error().
    SetTitle("Error").
    SetMessage("Failed to connect to server.").
    Show()

Use cases:

  • Error messages
  • Failure notifications
  • Exception handling
  • Critical issues

Example - Network error:

func fetchData(app *application.App, url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        app.Dialog.Error().
            SetTitle("Network Error").
            SetMessage(fmt.Sprintf("Failed to connect: %v", err)).
            Show()
        return nil, err
    }
    defer resp.Body.Close()

    return io.ReadAll(resp.Body)
}

Question dialog

Ask users questions and handle responses via button callbacks:

dialog := app.Dialog.Question().
    SetTitle("Confirm").
    SetMessage("Save changes before closing?")

save := dialog.AddButton("Save")
save.OnClick(func() {
    saveChanges()
})

dontSave := dialog.AddButton("Don't Save")
dontSave.OnClick(func() {
    // Continue without saving
})

cancel := dialog.AddButton("Cancel")
cancel.OnClick(func() {
    // Don't close
})

dialog.SetDefaultButton(save)
dialog.SetCancelButton(cancel)
dialog.Show()

Use cases:

  • Confirm actions
  • Yes/No questions
  • Multiple choice
  • User decisions

Example - Unsaved changes:

func closeDocument(app *application.App) {
    if !hasUnsavedChanges() {
        doClose()
        return
    }

    dialog := app.Dialog.Question().
        SetTitle("Unsaved Changes").
        SetMessage("Do you want to save your changes?")

    save := dialog.AddButton("Save")
    save.OnClick(func() {
        if saveDocument() {
            doClose()
        }
    })

    dontSave := dialog.AddButton("Don't Save")
    dontSave.OnClick(func() {
        doClose()
    })

    cancel := dialog.AddButton("Cancel")
    // Cancel button has no callback - just closes the dialog

    dialog.SetDefaultButton(save)
    dialog.SetCancelButton(cancel)
    dialog.Show()
}

dialog Options

Title and Message

dialog := app.Dialog.Info().
    SetTitle("Operation Complete").
    SetMessage("All files have been processed successfully.")

Best practices:

  • Title: Short, descriptive (2-5 words)
  • Message: Clear, specific, actionable
  • Avoid jargon: Use plain language

Buttons

Single button (Info/Warning/Error):

Info, warning, and error dialogs display a default “OK” button:

app.Dialog.Info().
    SetMessage("Done!").
    Show()

You can also add custom buttons:

dialog := app.Dialog.Info().
    SetMessage("Done!")
ok := dialog.AddButton("Got it!")
dialog.SetDefaultButton(ok)
dialog.Show()

Multiple buttons (Question):

Use AddButton() to add buttons, which returns a *Button you can configure:

dialog := app.Dialog.Question().
    SetMessage("Choose an action")

option1 := dialog.AddButton("Option 1")
option1.OnClick(func() {
    handleOption1()
})

option2 := dialog.AddButton("Option 2")
option2.OnClick(func() {
    handleOption2()
})

option3 := dialog.AddButton("Option 3")
option3.OnClick(func() {
    handleOption3()
})

dialog.Show()

Default and Cancel buttons:

Use SetDefaultButton() to specify which button is highlighted and triggered by Enter. Use SetCancelButton() to specify which button is triggered by Escape.

dialog := app.Dialog.Question().
    SetMessage("Delete file?")

deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
    performDelete()
})

cancelBtn := dialog.AddButton("Cancel")
// No callback needed - just dismisses dialog

dialog.SetDefaultButton(cancelBtn)  // Safe option as default
dialog.SetCancelButton(cancelBtn)   // Escape triggers Cancel
dialog.Show()

You can also use the fluent SetAsDefault() and SetAsCancel() methods on buttons:

dialog := app.Dialog.Question().
    SetMessage("Delete file?")

dialog.AddButton("Delete").OnClick(func() {
    performDelete()
})

dialog.AddButton("Cancel").SetAsDefault().SetAsCancel()

dialog.Show()

Best practices:

  • 1-3 buttons: Don’t overwhelm users
  • Clear labels: “Save” not “OK”
  • Safe default: Non-destructive action
  • Order matters: Most likely action first (except Cancel)

Custom Icon

Set a custom icon for the dialog:

app.Dialog.Info().
    SetTitle("Custom Icon Example").
    SetMessage("Using a custom icon").
    SetIcon(myIconBytes).
    Show()

Window Attachment

Attach to specific window:

dialog := app.Dialog.Question().
    SetMessage("Window-specific question").
    AttachToWindow(window)

dialog.AddButton("OK")
dialog.Show()

Benefits:

  • dialog appears on correct window
  • Parent window disabled whilst shown
  • Better multi-window UX

Complete Examples

Confirm Destructive Action

func deleteFiles(app *application.App, paths []string) {
    // Confirm deletion
    message := fmt.Sprintf("Delete %d file(s)?", len(paths))
    if len(paths) == 1 {
        message = fmt.Sprintf("Delete %s?", filepath.Base(paths[0]))
    }

    dialog := app.Dialog.Question().
        SetTitle("Confirm Delete").
        SetMessage(message)

    deleteBtn := dialog.AddButton("Delete")
    deleteBtn.OnClick(func() {
        // Perform deletion
        var errs []error
        for _, path := range paths {
            if err := os.Remove(path); err != nil {
                errs = append(errs, err)
            }
        }

        // Show result
        if len(errs) > 0 {
            app.Dialog.Error().
                SetTitle("Delete Failed").
                SetMessage(fmt.Sprintf("Failed to delete %d file(s)", len(errs))).
                Show()
        } else {
            app.Dialog.Info().
                SetTitle("Delete Complete").
                SetMessage(fmt.Sprintf("Deleted %d file(s)", len(paths))).
                Show()
        }
    })

    cancelBtn := dialog.AddButton("Cancel")
    dialog.SetDefaultButton(cancelBtn)
    dialog.SetCancelButton(cancelBtn)
    dialog.Show()
}

Quit Confirmation

func confirmQuit(app *application.App) {
    dialog := app.Dialog.Question().
        SetTitle("Quit").
        SetMessage("You have unsaved work. Are you sure you want to quit?")

    yes := dialog.AddButton("Yes")
    yes.OnClick(func() {
        app.Quit()
    })

    no := dialog.AddButton("No")
    dialog.SetDefaultButton(no)
    dialog.Show()
}

Update Dialog with Download Option

func showUpdateDialog(app *application.App) {
    dialog := app.Dialog.Question().
        SetTitle("Update").
        SetMessage("A new version is available. The cancel button is selected when pressing escape.")

    download := dialog.AddButton("📥 Download")
    download.OnClick(func() {
        app.Dialog.Info().SetMessage("Downloading...").Show()
    })

    cancel := dialog.AddButton("Cancel")

    dialog.SetDefaultButton(download)
    dialog.SetCancelButton(cancel)
    dialog.Show()
}

Custom Icon Question

func showCustomIconQuestion(app *application.App, iconBytes []byte) {
    dialog := app.Dialog.Question().
        SetTitle("Custom Icon Example").
        SetMessage("Using a custom icon").
        SetIcon(iconBytes)

    likeIt := dialog.AddButton("I like it!")
    likeIt.OnClick(func() {
        app.Dialog.Info().SetMessage("Thanks!").Show()
    })

    notKeen := dialog.AddButton("Not so keen...")
    notKeen.OnClick(func() {
        app.Dialog.Info().SetMessage("Too bad!").Show()
    })

    dialog.SetDefaultButton(likeIt)
    dialog.Show()
}

Best Practices

✅ Do

  • Be specific - “File saved to Documents” not “Success”
  • Use appropriate type - Error for errors, Warning for warnings
  • Provide context - Include relevant details
  • Use clear button labels - “Delete” not “OK”
  • Set safe defaults - Non-destructive action
  • Handle cancellation - User might close dialog

❌ Don’t

  • Don’t overuse - Interrupts workflow
  • Don’t use for frequent updates - Use notifications instead
  • Don’t use generic messages - “Error” tells nothing
  • Don’t ignore errors - Handle dialog.Show() errors
  • Don’t block unnecessarily - Consider async alternatives
  • Don’t use technical jargon - Plain language

Platform Differences

macOS

  • Sheet-style when attached to window
  • Standard keyboard shortcuts (⌘. for Cancel)
  • Follows system theme automatically
  • Accessibility built-in

Windows

  • Modal dialogs
  • TaskDialog appearance
  • Esc for Cancel
  • Follows system theme

Linux

  • GTK dialogs
  • Varies by desktop environment
  • Follows desktop theme
  • Standard keyboard navigation

Next Steps

File dialogs

Open, save, and folder selection.

Learn More →

Custom dialogs

Create custom dialog windows.

Learn More →

Notifications

Non-intrusive notifications.

Learn More →

Events

Use events for non-blocking communication.

Learn More →


Questions? Ask in Discord or check the dialog examples.

Edit page

Last updated: