In the last few lessons, we've focused on push notifications, which are initiated by a server and are essential for real-time communication. Now, we'll turn our attention to another crucial tool in your mobile app toolkit: local notifications. These are notifications scheduled and triggered entirely on the user's device, making them independent of a network connection and perfect for features like reminders, alarms, or countdowns.
Today, you will learn how to install and use the @capacitor/local-notifications plugin to schedule both immediate and delayed alerts on iOS and Android. This complements your knowledge of push notifications by giving you full control over client-side scheduling.
1. Installation and Basic Setup
First, we need to add the local notifications plugin to our project. As with other Capacitor plugins, this involves installing the package and syncing the native projects. Given your preference for bun, the commands are straightforward.
In your monorepo root, run:
bun add @capacitor/local-notifications
bunx cap sync
This installs the necessary web and native code. You can see the official installation instructions in the Capacitor documentation.
Local Notifications Capacitor Plugin API
This is the official API documentation for the plugin. It will be our primary reference.
Confirm the installation command under the Install section. Remember to substitute npm and npx with bun and bunx.
2. Permissions are Paramount
Just like with push notifications, your app must request the user's permission before it can display local notifications. The process is nearly identical, using the checkPermissions and requestPermissions methods. This is especially important on Android 13 and newer, where the permission is mandatory.
Here’s the standard flow you'd implement before attempting to schedule a notification:
import { LocalNotifications } from '@capacitor/local-notifications';
const requestNotificationPermission = async () => {
let permStatus = await LocalNotifications.checkPermissions();
if (permStatus.display === 'prompt') {
permStatus = await LocalNotifications.requestPermissions();
}
if (permStatus.display !== 'granted') {
throw new Error('User denied permissions!');
}
return true;
};
This function first checks the current status. If permission hasn't been requested yet (prompt), it asks the user. If permission is ultimately not granted, it throws an error that you can catch to update your UI accordingly.
3. Scheduling Notifications
The core of this plugin is the schedule() method. It takes an object containing a notifications array, allowing you to schedule one or more notifications at once.
Immediate Notifications
To schedule a notification that appears instantly (or as close to instantly as the OS allows), you simply define its basic properties without a schedule object. Each notification needs a unique id, which you can use later to update or cancel it.
Let's look at the structure of a single notification object.
Local Notifications Capacitor Plugin API
The LocalNotificationSchema interface defines all possible properties for a notification.
In the API documentation, navigate to the "Interfaces" section and find LocalNotificationSchema. Review the core properties like title, body, and id. Notice the schedule property, which we'll use next.
Here is an example of a React component that schedules an immediate notification when a button is clicked.
import React from 'react';
import { IonButton, isPlatform } from '@ionic/react';
import { LocalNotifications } from '@capacitor/local-notifications';
const NotificationScheduler: React.FC = () => {
const scheduleImmediateNotification = async () => {
if (!isPlatform('mobile')) return;
try {
await LocalNotifications.requestPermissions(); // Best practice to request here if not done elsewhere
await LocalNotifications.schedule({
notifications: [
{
title: "Immediate Alert",
body: "This notification was scheduled from the app.",
id: 1, // A unique integer
extra: {
// You can pass custom data here
source: 'app-button-click'
}
}
]
});
} catch (error) {
console.error("Error scheduling notification:", error);
}
};
return (
<IonButton onClick={scheduleImmediateNotification}>
Schedule Immediate Notification
</IonButton>
);
};
export default NotificationScheduler;
Delayed and Recurring Notifications
To schedule a notification for a future time, you use the schedule property within the notification object. This property itself is an object that specifies the timing. The most common way to do this is with the at property, which takes a Date object.
Here’s how you would schedule a notification to appear 5 seconds in the future:
await LocalNotifications.schedule({
notifications: [
{
title: "Delayed Alert",
body: "This appeared 5 seconds after you clicked the button.",
id: 2,
schedule: {
at: new Date(Date.now() + 5000) // 5 seconds from now
}
}
]
});
This simple mechanism is incredibly powerful for building features like reminders.

The schedule object also supports recurring notifications. You can specify repeats or use cron-like syntax.
Local Notifications Capacitor Plugin API
The Schedule interface documentation explains the different ways you can time your notifications.
Find the Schedule interface definition. Pay attention to the at property for delayed alerts and briefly review the repeats and every properties to see how recurring schedules are constructed.
For a practical demonstration of scheduling both basic and timed notifications, the following video is very helpful.
Creating Capacitor Local Notifications with Sound, Icons & Action Buttons
Simon Grimm's tutorial provides a clear walkthrough of the LocalNotifications plugin.
First, watch the segment on scheduling a basic notification, where he covers permissions and constructs the notification object. Then, skip to the section on scheduling timed notifications to see the schedule.at property in action.
4. Platform-Specific Configurations
While Capacitor abstracts many platform differences, local notifications have a few important platform-specific details you should be aware of.
Android: Notification Channels
Since Android 8.0 (API level 26), all notifications must be assigned to a "channel". Channels allow users to manage notification settings for your app in granular detail (e.g., disable marketing alerts but keep transactional ones). If you don't create a channel, your notifications may not be delivered on modern Android devices.
You should create your channels when the app starts.
import { LocalNotifications } from '@capacitor/local-notifications';
import { isPlatform } from '@ionic/react';
const createNotificationChannels = async () => {
if (isPlatform('android')) {
await LocalNotifications.createChannel({
id: 'default_channel',
name: 'Default',
description: 'General notifications',
importance: 4, // Corresponds to IMPORTANCE_DEFAULT
visibility: 1, // Corresponds to VISIBILITY_PUBLIC
});
}
};
// Call this in your main App component's useEffect
Once a channel is created, you can assign a notification to it using the channelId property in LocalNotificationSchema. If you don't specify one, it will use a default channel Capacitor creates.
You can find more details on channels in the API documentation.
Local Notifications Capacitor Plugin API
The documentation explains the createChannel method.
Locate the createChannel(...) method in the API list. Review the method signature and understand that this is an Android-only requirement.
Android: Exact Alarms & Icons
- Exact Alarms: For time-critical apps like alarm clocks, Android 12+ requires the
SCHEDULE_EXACT_ALARMpermission inAndroidManifest.xmlforschedule.atto be precise. - Icons: You can customize the small notification icon and its color via
capacitor.config.ts. This requires adding the icon assets to the native Android project'sres/drawabledirectory.
The video from Simon Grimm also provides an excellent walkthrough of adding custom icons and sounds for Android, which can be useful for adding a professional polish.
Conclusion
You now have the ability to schedule notifications directly from your application's client-side code. This is a fundamental capability for many mobile apps, enabling you to engage users without relying on a server or an internet connection.
Here are the key takeaways:
- Local notifications are scheduled and delivered entirely on the device, contrasting with server-sent push notifications.
- The
@capacitor/local-notificationsplugin is installed and synced like any other Capacitor plugin. - You must request user permission with
requestPermissions()before scheduling any notifications. - The
LocalNotifications.schedule()method is used to create both immediate and delayed alerts. - For delayed alerts, the
schedule: { at: new Date(...) }property is used. - On Android, creating notification channels via
createChannel()is essential for modern devices.
In our next lesson, we'll close the loop on local notifications by learning how to handle user interactions. We'll implement listeners that fire when a user taps on a notification, allowing you to deep-link them to a specific part of your React application.
Can't find a good explanation? Sign up and we'll make it for you
Sign up