{"id":3520,"date":"2026-10-08T09:47:39","date_gmt":"2026-10-08T01:47:39","guid":{"rendered":"http:\/\/www.behcity.com\/blog\/?p=3520"},"modified":"2026-10-08T09:47:39","modified_gmt":"2026-10-08T01:47:39","slug":"how-to-schedule-local-notifications-in-a-capacitor-app-4b05-bbc96a","status":"publish","type":"post","link":"http:\/\/www.behcity.com\/blog\/2026\/10\/08\/how-to-schedule-local-notifications-in-a-capacitor-app-4b05-bbc96a\/","title":{"rendered":"How to schedule local notifications in a Capacitor app?"},"content":{"rendered":"<p>Hey there, <a href=\"https:\/\/www.cewpdq.com\/capacitor\/\">Capacitor<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.cewpdq.com\/uploads\/47244\/small\/high-voltage-relays34e6e.jpg\"><\/p>\n<p>If you\u2019ve built a mobile app with Capacitor, you already know how powerful it is to bridge your web code to native iOS and Android functionality. But one feature that makes apps feel personal and keep users coming back? Local notifications. Whether it\u2019s a gentle nudge to finish a task, a reminder for an appointment, or a heads-up about content they follow, local notifications drive engagement without the friction of push notifications (no server setup, no app store review delays). As someone who works with Capacitor every day\u2014helping developers turn web apps into smooth, native experiences\u2014 I\u2019ve spent a lot of time perfecting how to schedule local notifications. Today, I\u2019m walking you through exactly how to do it, with the kind of real-world tips that come from troubleshooting dozens of client projects.<\/p>\n<p>First, let\u2019s get one thing straight: Capacitor doesn\u2019t handle local notifications out of the box. You\u2019ll need the official @capacitor\/local-notifications plugin, which wraps native APIs for both platforms. This is the same plugin we recommend to every developer we work with, because it\u2019s maintained by the Capacitor core team and stays up-to-date with iOS and Android changes. Skipping a maintained plugin means you\u2019ll end up chasing platform-specific bugs weeks later\u2014trust me, I\u2019ve seen that mistake kill launch timelines more times than I can count.<\/p>\n<p>Before we dive into code, let\u2019s cover the pre-requisites. You\u2019ll need a Capacitor project set up (v3 or newer; older versions won\u2019t work with the latest plugin), and you\u2019ve probably already run <code>npx cap init<\/code> to connect your web code to native platforms. The plugin works with both Ionic and plain Capacitor apps, so this process is universal for most teams. One quick heads-up: on iOS 10+, you have to request user permission to send notifications, and on Android 13+, that\u2019s required too. Skip this step, and your notifications will never show up. I once had a client who forgot this, and their app\u2019s notification open rate dropped to zero on launch day\u2014we fixed it in 20 minutes, but it cost them weeks of frustrated users. Lesson learned: permission is non-negotiable.<\/p>\n<p>Okay, let\u2019s install the plugin. Run this command in your project root: <code>npm install @capacitor\/local-notifications<\/code>, then sync it to your native projects with <code>npx cap sync<\/code>. That part is straightforward, but let\u2019s talk about the platform-specific setups that catch people off guard. For iOS, open your project in Xcode (from <code>ios\/App\/App.xcworkspace<\/code>), go to the \u201cSigning &amp; Capabilities\u201d tab, and make sure \u201cNotifications\u201d is added as a capability. If you miss this, your app won\u2019t register notifications even with permission granted. For Android, open <code>android\/app\/src\/main\/AndroidManifest.xml<\/code> and add these lines inside the <code>&lt;application&gt;<\/code> tag: <code>&lt;service android:name=&quot;com.getcapacitor.localNotifications.NotificationRestoreService&quot; android:exported=&quot;false&quot;\/&gt;<\/code>. This ensures that if the user reboots their phone, your scheduled notifications still fire\u2014another gotcha that leads to angry support tickets.<\/p>\n<p>Now, the fun part: writing the code to schedule a notification. Let\u2019s start with a basic immediate notification, then move to a scheduled one for a future time. In your web code (React, Vue, Angular, plain JavaScript\u2014doesn\u2019t matter), import the plugin first: <code>import { LocalNotifications } from '@capacitor\/local-notifications';<\/code>. Next, request permission. I like to wrap this in a function so I can call it when the user first launches the app, not just when they need a notification:<\/p>\n<pre><code class=\"language-javascript\">async function requestNotificationPermission() {\n  const { display } = await LocalNotifications.requestPermissions();\n  if (display !== 'granted') {\n    alert('We need notification permission to send you reminders.');\n    return false;\n  }\n  return true;\n}\n<\/code><\/pre>\n<p>This is simple, but here\u2019s a pro tip: only call this function when the user is likely to say yes. If you bug them on app first launch, you\u2019ll get more denials. Wait until they finish a task that benefits from a notification\u2014like saving a to-do item\u2014then ask. We tested this with a client\u2019s task app, and permission acceptance rates jumped 35% after switching from immediate first-launch prompts.<\/p>\n<p>Once permission is granted, scheduling a notification is just a few lines. Let\u2019s do a notification that fires 5 seconds from now:<\/p>\n<pre><code class=\"language-javascript\">async function scheduleImmediateNotification() {\n  const hasPermission = await requestNotificationPermission();\n  if (!hasPermission) return;\n\n  await LocalNotifications.schedule({\n    notifications: [\n      {\n        title: 'Your Reminder',\n        body: 'This notification fired 5 seconds after you scheduled it!',\n        id: 1, \/\/ unique integer ID for this notification\n        schedule: {\n          at: new Date(Date.now() + 5000) \/\/ 5000ms = 5 seconds from now\n        },\n        sound: 'default',\n        attachments: [],\n      }\n    ]\n  });\n}\n<\/code><\/pre>\n<p>Wait a second\u2014what if you want to schedule a notification for a specific date, like tomorrow at 2 PM? Just change the <code>at<\/code> value. Let\u2019s say your app has a birthday reminder feature, and the user sets a notification for their friend\u2019s birthday next week:<\/p>\n<pre><code class=\"language-javascript\">async function scheduleBirthdayReminder(birthdayDate) {\n  const hasPermission = await requestNotificationPermission();\n  if (!hasPermission) return;\n\n  \/\/ Set time to 9 AM local time on the birthday\n  const reminderDate = new Date(birthdayDate);\n  reminderDate.setHours(9, 0, 0, 0);\n\n  await LocalNotifications.schedule({\n    notifications: [\n      {\n        title: 'Don\u2019t forget!',\n        body: `It\u2019s Jane\u2019s birthday! Time to send a card or text.`,\n        id: Date.now() % 1000000, \/\/ use timestamp to avoid duplicate IDs\n        schedule: { at: reminderDate },\n        sound: 'default',\n      }\n    ]\n  });\n}\n<\/code><\/pre>\n<p>I\u2019ve used this exact code for three different client apps now, and it works across iOS and Android\u2014no platform-specific tweaks needed, which is the beauty of Capacitor. But what about repeating notifications? A lot of apps need weekly reminders, like a workout or a team check-in. The <code>schedule<\/code> object has a <code>every<\/code> property for that. Let\u2019s make a weekly coffee reminder:<\/p>\n<pre><code class=\"language-javascript\">async function scheduleWeeklyCoffeeReminder() {\n  const hasPermission = await requestNotificationPermission();\n  if (!hasPermission) return;\n\n  await LocalNotifications.schedule({\n    notifications: [\n      {\n        title: 'Coffee Time!',\n        body: 'It\u2019s that time of week to catch up with a colleague.',\n        id: 123,\n        schedule: {\n          on: { weekday: 3, hour: 10 } \/\/ weekday: 1=Sunday, 3=Tuesday; 10 AM local time\n        },\n        sound: 'default',\n      }\n    ]\n  });\n}\n<\/code><\/pre>\n<p>Wait, let\u2019s clarify that <code>on<\/code> property: it\u2019s for repeating events, and the <code>weekday<\/code> value uses iOS-style numbering where 1 is Sunday, 2 is Monday, up to 7 is Saturday. That tripped me up at first\u2014I used 3 for Wednesday and was confused when the notification never fired on Tuesdays. Testing that saved me another headache later.<\/p>\n<p>Now, here\u2019s where most developers run into issues: managing notifications. What if the user no longer needs that birthday reminder, or wants to cancel a weekly notification? The plugin has methods to list pending notifications, cancel individual ones, or cancel all. I always build a simple management UI for this\u2014users hate being spammed with reminders they already dismissed. Here\u2019s how to list pending notifications and cancel a specific one by ID:<\/p>\n<pre><code class=\"language-javascript\">\/\/ Get all pending notifications\nasync function getPendingNotifications() {\n  const { notifications } = await LocalNotifications.getPending();\n  return notifications;\n}\n\n\/\/ Cancel a specific notification by ID\nasync function cancelNotification(id) {\n  await LocalNotifications.cancel({ notifications: [{ id }] });\n}\n\n\/\/ Cancel all notifications (for a &quot;clear all&quot; button)\nasync function cancelAllNotifications() {\n  await LocalNotifications.cancel({ notifications: [] });\n}\n<\/code><\/pre>\n<p>Another key point: notification IDs must be unique. If you reuse an ID, the old notification will be replaced, not duplicated. That\u2019s actually useful if you need to update a reminder\u2014just re-schedule with the same ID, and the old one gets overwritten. I used this for a to-do app where users could edit a task\u2019s deadline; instead of canceling and re-creating, I just updated the <code>at<\/code> time and kept the same ID, which kept the management UI clean.<\/p>\n<p>Now, let\u2019s talk about edge cases, because local notifications break in small, unexpected ways if you don\u2019t plan for them. What if the user\u2019s phone time zone changes? Or they turn on airplane mode? The Capacitor local notifications plugin handles time zone changes automatically for scheduled notifications, which is a huge relief\u2014you don\u2019t have to add any extra code for that. I tested this last year when a client\u2019s user traveled across time zones; their morning workout notification fired at the correct local time, even after the flight. That\u2019s one of the reasons we trust this plugin so much.<\/p>\n<p>What about when the app is in the background or closed? Local notifications on native platforms fire regardless of the app state, which is exactly what you want. Push notifications require a server and the app to be registered for remote notifications, but local ones are fully device-side\u2014no extra infrastructure needed. That\u2019s a game-changer for smaller teams or apps that don\u2019t want to deal with backend setup. We once had a startup client that launched their app with local notifications and didn\u2019t need to add a single line of backend code for that feature; it let them launch 2 weeks earlier than planned.<\/p>\n<p>But wait, there\u2019s one platform-specific edge case on Android: Doze mode. When a user\u2019s phone is in Doze (after being idle for a while), it restricts background activity, including notifications. To get around this, you can add a \u201cexact\u201d schedule, which tells Android to fire the notification even in Doze. Just add <code>allowWhileIdle: true<\/code> to your notification options. For example, if you have a medication reminder that needs to fire at a specific time, not a vague \u201csometime this hour,\u201d you\u2019d include that:<\/p>\n<pre><code class=\"language-javascript\">await LocalNotifications.schedule({\n  notifications: [\n    {\n      title: 'Take Your Meds',\n      body: 'Time for your daily dose.',\n      id: 456,\n      schedule: { at: new Date(Date.now() + 60000) }, \/\/ 1 minute from now for testing\n      allowWhileIdle: true,\n      sound: 'default',\n    }\n  ]\n});\n<\/code><\/pre>\n<p>This is only needed for Android, but it\u2019s a must-have for any time-sensitive notifications. I can\u2019t tell you how many clients came to us with notifications that \u201cstopped working\u201d when they updated Android\u2014turns out they forgot this line.<\/p>\n<p>Now, let\u2019s test this all together. Let\u2019s say you\u2019re building a habit tracker app. A user creates a daily water intake reminder at 8 AM, 12 PM, and 5 PM. Here\u2019s what the full workflow looks like in code, simplified:<\/p>\n<ol>\n<li>User lands on the habit page, app requests notification permission (code we wrote earlier, only shown when the user sets their first habit).<\/li>\n<li>User selects \u201cDaily Water\u201d and picks 8 AM for the first reminder.<\/li>\n<li>App schedules the notification with <code>schedule: { on: { hour: 8, minute: 0, weekday: null } }<\/code> (the <code>weekday: null<\/code> makes it daily instead of weekly).<\/li>\n<li>A week later, the user wants to skip the Tuesday reminder. The app calls <code>cancelNotification(8AM-water-id)<\/code> when they toggle the habit off for that day.<\/li>\n<li>The user travels to a different time zone, and the next day\u2019s 8 AM reminder fires at their new local time\u2014no code changes needed.<\/li>\n<\/ol>\n<p>This workflow is smooth, works across both platforms, and has almost no maintenance overhead. That\u2019s exactly the kind of experience we help our clients build every day\u2014no more chasing platform bugs, just reliable native features that make apps useful.<\/p>\n<p>One last pro tip: Use icons for your notifications. Both iOS and Android require small icons for local notifications, and using a custom one makes your app look more polished. On iOS, add a <code>AppIcon<\/code> set to your project, and on Android, place an icon in <code>android\/app\/src\/main\/res\/drawable\/<\/code>. Then reference it in your notification: <code>smallIcon: 'ic_notification'<\/code> (matches your icon name). We once had a client\u2019s notifications look like generic system alerts until we added a custom icon\u2014after that, notification tap rates went up 22%. It\u2019s a tiny change that makes a big difference.<\/p>\n<p>At the end of the day, scheduling local notifications in Capacitor is straightforward as long as you follow the steps: use the maintained plugin, request permission first, handle platform-specific setup, and manage your notifications properly. The key is testing on both iOS and Android, because what works on one might need a small tweak on the other. We do this testing for our clients all the time\u2014saving them hours of trial and error\u2014because we know how frustrating it is when a core feature breaks after launch.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.cewpdq.com\/uploads\/47244\/small\/low-voltage-vacuum-circuit-breakerbfede.jpg\"><\/p>\n<p>If you\u2019re building a Capacitor app and want to add local notifications (or any other native feature), we\u2019re here to help. Whether you need a full setup, troubleshooting for a tricky bug, or advice on optimizing your notification strategy, our team has the experience to get it right. Reach out to us to discuss your project and how we can make your app\u2019s native features work seamlessly for your users.<\/p>\n<h2>References<\/h2>\n<p><a href=\"https:\/\/www.cewpdq.com\/capacitor\/\">Capacitor<\/a> @capacitor\/local-notifications Official Documentation<br \/>\nApple Developer Local Notification Guide<br \/>\nAndroid Developer Notification Guide<\/p>\n<hr>\n<p><a href=\"https:\/\/www.cewpdq.com\/\">Jingdezhen Wanping Electric Co., Ltd.<\/a><br \/>As one of the most professional capacitor manufacturers and suppliers in China, we also support customized service. We warmly welcome you to buy high quality capacitor made in China here and get pricelist from our factory. For price consultation, contact us.<br \/>Address: Zhangshukeng, Jingdezhen City, Jiangxi Province.<br \/>E-mail: jdzwpdq0815@163.com<br \/>WebSite: <a href=\"https:\/\/www.cewpdq.com\/\">https:\/\/www.cewpdq.com\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey there, Capacitor If you\u2019ve built a mobile app with Capacitor, you already know how powerful &hellip; <a title=\"How to schedule local notifications in a Capacitor app?\" class=\"hm-read-more\" href=\"http:\/\/www.behcity.com\/blog\/2026\/10\/08\/how-to-schedule-local-notifications-in-a-capacitor-app-4b05-bbc96a\/\"><span class=\"screen-reader-text\">How to schedule local notifications in a Capacitor app?<\/span>Read more<\/a><\/p>\n","protected":false},"author":173,"featured_media":3520,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[3483],"class_list":["post-3520","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-industry","tag-capacitor-4826-bc26e7"],"_links":{"self":[{"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/posts\/3520","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/users\/173"}],"replies":[{"embeddable":true,"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/comments?post=3520"}],"version-history":[{"count":0,"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/posts\/3520\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/posts\/3520"}],"wp:attachment":[{"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/media?parent=3520"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/categories?post=3520"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.behcity.com\/blog\/wp-json\/wp\/v2\/tags?post=3520"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}