Back to Insights

React Native · Published 2026-08-15 · 6 min read · Waris Labs

How Local Notifications Work in React Native When the App Is Closed

Learn how scheduled local notifications continue to work on Android and iOS when a React Native app is foregrounded, backgrounded, or terminated.

Published by Waris Labs, a software engineering studio publishing practical guides on React Native, Firebase, cloud systems, app publishing, and AI automation.

Waris Labs Insights are educational technical notes. Verify platform requirements, security impact, and production behavior before applying code or configuration changes.

Local notifications are often misunderstood because the JavaScript code that schedules them and the operating system service that displays them are not the same thing. In a React Native app, JavaScript prepares the request, but Android or iOS owns the actual delivery once the notification has been scheduled.

That distinction is important for reminder apps, habit trackers, medication alerts, booking reminders, fitness streaks, and any workflow where a user expects the phone to notify them even if the app is no longer visible.

Quick Answer

Yes. A properly scheduled local notification can still be delivered after a React Native app is backgrounded or terminated because Android or iOS owns the scheduled delivery after the native notification request is accepted. Exact behavior still depends on permissions, Android channels, iOS notification settings, exact alarm needs, library behavior, and release-build testing.

Key Takeaways

  • A native scheduled notification is different from a JavaScript timer.
  • Local notifications fit reminders already known on the device; push notifications fit server-known events.
  • Android notification channels and runtime permission behavior are platform requirements, not Waris Labs preferences.
  • Waris Labs recommends testing notification behavior in release mode across foreground, background, terminated, reboot, permission-denied, and channel-disabled states.

Local Notifications vs Push Notifications

A local notification is created by the app on the device. The app asks the native notification API to display something now or later. Once accepted, the schedule is stored by the operating system.

A push notification is created outside the device. A server sends a message through Firebase Cloud Messaging on Android or Apple Push Notification service on iOS. The push service then delivers it to the device when possible.

Use local notifications when the trigger is already known on the phone:

  • Daily workout reminder at 7 AM
  • Hydration reminder every few hours
  • Countdown timer completion
  • Follow-up reminder created by a local task

Use push notifications when the trigger depends on server-side knowledge:

  • A coach sends a message
  • A backend detects an account event
  • A team member comments on shared data
  • Inventory or price information changes on the server

For a larger server-driven notification system, see Scheduling Push Notifications at Scale with Firebase Cloud Functions.

What Happens in Each App State

When the app is foregrounded, JavaScript is running and your UI is visible. You can decide whether to show an in-app banner, trigger a local notification, update state, or suppress the alert. Some notification libraries require a foreground notification handler because many users do not want a full system alert while they are already using the screen.

When the app is backgrounded, the app process may still exist, but it should not be treated as a reliable timer. Mobile operating systems aggressively manage background execution to protect battery, memory, and performance. JavaScript may be paused, throttled, or stopped.

When the app is terminated, JavaScript is not running. If a notification was already scheduled natively, the operating system can still display it. If your plan depends on JavaScript waking up at the exact future time to calculate the notification, that plan is fragile.

The practical rule is simple:

Schedule important reminders with the native notification system before you need them.

Why JavaScript Does Not Need to Keep Running

In React Native, JavaScript sends a scheduling request to native code. Native code passes the request to Android or iOS. The operating system then stores the trigger and notification payload. That payload might include title, body, sound, badge behavior, category, channel, and data used for deep linking.

The JavaScript thread does not need to stay alive for a scheduled local notification to appear. This is the same reason a native Android or iOS alarm can show after the app screen is gone.

The opposite is also true. A JavaScript timer is not enough:

setTimeout(() => {
  showReminder();
}, 1000 * 60 * 60 * 8);

That timer can work while the app is open, but it is not a durable reminder system. If the app is closed, killed under memory pressure, updated, or restarted, the timer is gone.

Practical Scheduling Example

This example uses a small scheduling boundary. The exact library API may vary, but the product architecture is the point: permission first, Android channel setup, then schedule through the native notification API.

import { Platform } from 'react-native';
import * as Notifications from 'expo-notifications';

export async function scheduleWorkoutReminder(reminderAt: Date) {
  const permission = await Notifications.requestPermissionsAsync();

  if (!permission.granted) {
    return { scheduled: false, reason: 'permission-denied' };
  }

  if (Platform.OS === 'android') {
    await Notifications.setNotificationChannelAsync('workout-reminders', {
      name: 'Workout reminders',
      importance: Notifications.AndroidImportance.DEFAULT,
    });
  }

  const id = await Notifications.scheduleNotificationAsync({
    content: {
      title: 'Workout reminder',
      body: 'Your next session is ready when you are.',
      data: { screen: 'WorkoutPlan' },
    },
    trigger: {
      type: Notifications.SchedulableTriggerInputTypes.DATE,
      date: reminderAt,
      channelId: Platform.OS === 'android' ? 'workout-reminders' : undefined,
    },
  });

  return { scheduled: true, id };
}

For Expo projects, the official Expo Notifications documentation is the right reference for current package behavior and platform notes: Expo Notifications.

Permissions Should Be Part of the Product Flow

Do not ask for notification permission on the first frame of the app unless the entire product cannot function without it. A better flow is to ask after the user creates a reminder, chooses a habit schedule, or reaches a screen where the benefit is obvious.

Good permission prompts usually answer three questions:

  • What will the app remind the user about?
  • How often will reminders appear?
  • Can the user change the setting later?

On Android, notification channels matter. A channel lets users control a group of notifications. Create channels before requesting push tokens or scheduling channel-specific reminders. For local reminders, separate channels such as workout-reminders, meal-reminders, and account-alerts can make settings more understandable.

Battery Optimization Considerations

Battery optimization does not usually stop a properly scheduled notification from being delivered, but it can affect background work around it. For example, an app might not be allowed to wake up, fetch fresh content, and compute a new reminder exactly when it wants.

Avoid designs that require background JavaScript to run frequently. Instead:

  • Schedule the next known reminders while the user is active.
  • Store reminder preferences locally.
  • Reschedule when preferences change.
  • Reconcile schedules when the app opens.
  • Keep notification payloads small.

If a reminder depends on server-side state, use a push architecture instead of trying to force local scheduling to behave like a backend job.

Common Mistakes

The biggest mistake is relying on setTimeout, setInterval, or an in-memory queue for a future reminder. Those are UI-session tools, not app-lifecycle tools.

Another common mistake is scheduling too many reminders without canceling old ones. If a user changes a workout time from 7 AM to 8 AM, cancel the previous scheduled notification and create a new one. Keep the notification ID so the app can update or cancel the correct entry.

Teams also forget to test real lifecycle states. Debug builds, Expo Go, development builds, and release builds can behave differently. Test at least these cases:

  • App open
  • App in background
  • App swiped away
  • Device restarted
  • Timezone changed
  • Notification permission denied
  • Android channel disabled

Before submitting to Google Play, include notification behavior in your release checklist. The broader release flow is covered in How to Prepare a React Native App for Google Play Production.

Official Documentation

Conclusion

Local notifications work after a React Native app is closed because the operating system, not JavaScript, owns the scheduled delivery. Build the feature around that boundary. Ask for permission at the right moment, create Android channels, schedule native reminders ahead of time, cancel stale reminders, and test the release build on real lifecycle paths.

When you treat local notifications as a native schedule instead of a JavaScript timer, reminder features become much more predictable.

Related Articles