# How to Implement Cross-Platform System Notifications in a Tauri App

> Implement cross-platform system notifications in your Tauri app using a unified JavaScript and Rust API. Send native alerts across macOS Windows and Linux without platform specific code.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-11

---

**Tauri provides a unified JavaScript and Rust API that abstracts native notification systems across macOS, Windows, and Linux, allowing you to send system-level alerts from your frontend or backend code without platform-specific dependencies.**

Meetily, an open-source meeting recorder built with Tauri, leverages this architecture to deliver privacy-first desktop notifications. By using the `@tauri-apps/api/notification` module on the frontend and `tauri::api::notification` on the Rust backend, you can implement native system alerts that work consistently across all supported operating systems while keeping all data processing on-device.

## How Tauri Bridges Native Notification Systems

Tauri delegates notification rendering to each operating system's native APIs through a centralized bridge. When you invoke a notification from JavaScript, Tauri serializes the request via IPC and dispatches it through the appropriate platform-specific implementation on the Rust side.

The platform mapping works as follows:

- **macOS**: Uses `NSUserNotificationCenter` via Objective-C bindings handled in `tauri::api::notification::macos`
- **Windows**: Uses Win32 Toast notifications (`IToastNotification`) via `tauri::api::notification::windows`
- **Linux**: Uses freedesktop.org Desktop Notifications via the `notify-rust` crate in `tauri::api::notification::linux`

This architecture ensures **zero external service dependencies**, preserving user privacy by generating notifications entirely on-device.

## Frontend Implementation with TypeScript

### Sending Notifications from the UI

Import the `Notification` constructor from `@tauri-apps/api/notification` to create alerts directly from your React or TypeScript code. In Meetily's [`frontend/src/app/page.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/app/page.tsx), you can implement meeting-specific notifications like this:

```typescript
import { Notification } from '@tauri-apps/api/notification';

export const showMeetingStarted = async (meetingName: string) => {
  await new Notification(`🗓️ ${meetingName}`, {
    body: 'The meeting has begun – recording is active.',
    icon: 'icons/meeting.png',
    actions: [{ identifier: 'open', title: 'Open Meetily' }],
  });
};

```

The `Notification` constructor accepts a title string and an options object that supports `body`, `icon`, and `actions` parameters. These map directly to native notification capabilities on each platform.

### Handling User Actions

To respond when users click notification buttons, use Tauri's event listener system:

```typescript
import { listen } from '@tauri-apps/api/event';

listen('notification-clicked', (event) => {
  const { identifier } = event.payload as { identifier: string };
  if (identifier === 'open') {
    window.location.href = '/';
  }
});

```

This pattern allows the UI to react to notification interactions even when the application window is not focused.

## Backend Implementation in Rust

### Creating a Notification Helper

For Rust-side code paths (such as the recording manager), create a centralized helper in [`frontend/src-tauri/src/notifications.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/notifications.rs):

```rust
use tauri::{AppHandle, Manager};
use tauri::api::notification::Notification;

/// Sends a system notification from Rust (useful for non‑UI code paths)
pub async fn notify_meeting_started(app: &AppHandle, name: &str) -> Result<(), tauri::Error> {
    Notification::new("Meeting Started")
        .body(&format!("{} is now recording.", name))
        .icon("icons/meeting.png")
        .show(app)?;
    Ok(())
}

```

The Rust API returns a `Result<(), tauri::Error>` that can be awaited from the frontend, allowing you to handle permission failures or system errors gracefully.

### Registering Commands

Expose the notification functionality to your frontend by registering a command in [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs):

```rust
#[tauri::command]
async fn start_meeting_notification(
    app: tauri::AppHandle, 
    meeting_name: String
) -> Result<(), String> {
    notifications::notify_meeting_started(&app, &meeting_name)
        .await
        .map_err(|e| e.to_string())
}

```

Then invoke this command from TypeScript:

```typescript
import { invoke } from '@tauri-apps/api/tauri';

await invoke('start_meeting_notification', { meetingName: 'Team Standup' });

```

## Integration Points in Meetily

The notification system integrates with Meetily's existing architecture at these key locations:

- **[`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs)**: Registers the notification command and sets up the Tauri application shell
- **[`frontend/src-tauri/src/audio/recording_manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_manager.rs)**: Can trigger notifications when recording begins or ends
- **[`frontend/src-tauri/src/database/setup.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/setup.rs)**: Demonstrates the event emission pattern used for notification callbacks
- **[`frontend/src/app/page.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/app/page.tsx)**: Contains the UI logic for triggering meeting-related alerts

By placing notification logic in [`src-tauri/src/notifications.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/notifications.rs), you create a reusable module that both the audio recording manager and UI components can access.

## Platform-Specific Considerations

### macOS Permission Requirements

Before sending the first notification on macOS, you must request **User Notification** permissions. While Tauri handles the notification display, you need to ensure your `Info.plist` contains the appropriate permissions, or use `tauri-plugin-os` to query authorization status before calling the notification API.

### Windows and Linux Support

Windows automatically supports Toast notifications without additional configuration. Linux requires a notification daemon supporting the freedesktop.org specification (present in GNOME, KDE, and most modern distributions). The `notify-rust` crate handles the D-Bus communication automatically.

All platforms support the **async-friendly** pattern shown above, ensuring non-blocking notification delivery that won't freeze your UI during the IPC roundtrip.

## Summary

- **Tauri's notification API** abstracts macOS `NSUserNotificationCenter`, Windows Toast notifications, and Linux Desktop Notifications behind a single interface
- **Frontend code** uses `@tauri-apps/api/notification` to create alerts with titles, bodies, icons, and action buttons
- **Backend code** uses `tauri::api::notification::Notification` in Rust to send alerts from non-UI processes like the recording manager
- **File locations** in Meetily include [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs) for command registration and [`frontend/src-tauri/src/audio/recording_manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_manager.rs) for integration triggers
- **Permission handling** is required on macOS but automatic on Windows and Linux
- **Event listeners** capture user interactions with notification buttons via the `notification-clicked` event

## Frequently Asked Questions

### Do I need to install additional dependencies for Tauri notifications?

No. Notification support is built into the core Tauri library. Both `@tauri-apps/api/notification` for the frontend and `tauri::api::notification` for the backend are available without adding extra crates or npm packages. The `notify-rust` crate used for Linux support is bundled with Tauri's core dependencies.

### Can I send notifications when the Tauri window is closed?

Yes, but only if your Tauri application is configured to run in the background. Notifications require the Rust process to remain active. You can keep the process running by preventing exit in your `tauri::Builder` setup or by using the system tray plugin, then calling the notification commands from the backend even when windows are closed.

### How do I handle notification permission errors on macOS?

Wrap your notification calls in try-catch blocks (JavaScript) or match on the `Result` type (Rust). If permissions are denied, Tauri will return an error that you can handle gracefully. You can check permission status beforehand using `tauri-plugin-os` or by attempting to send a test notification and catching the specific permission-denied error variant.

### What's the difference between frontend and backend notification calls?

Frontend notifications (using `new Notification()`) execute immediately in the browser context and are suitable for UI-driven events. Backend notifications (using `Notification::new().show()`) execute in the Rust runtime and are ideal for background processes like the recording manager in [`frontend/src-tauri/src/audio/recording_manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_manager.rs). Both ultimately use the same native APIs, but backend calls can be triggered from anywhere in your Rust code, not just the UI thread.