Message dialogs
Message dialogs
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “dialog Options”Title and Message
Section titled “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
Section titled “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 defaultdialog.SetCancelButton(cancelBtn) // Escape triggers Canceldialog.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
Section titled “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
Section titled “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
Section titled “Complete Examples”Confirm Destructive Action
Section titled “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
Section titled “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
Section titled “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
Section titled “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
<CardGrid> <Card title="File dialogs" icon="document"> Open, save, and folder selection.
[Learn More →](/features/dialogs/file) </Card>
<Card title="Custom dialogs" icon="puzzle"> Create custom dialog windows.
[Learn More →](/features/dialogs/custom) </Card>
<Card title="Notifications" icon="bell"> Non-intrusive notifications.
[Learn More →](/features/notifications) </Card>
<Card title="Events" icon="star"> Use events for non-blocking communication.
[Learn More →](/features/events/system) </Card></CardGrid>
---
**Questions?** Ask in [Discord](https://discord.gg/JDdSxwjhGf) or check the [dialog examples](https://github.com/wailsapp/wails/tree/master/v3/examples/dialogs).