Push Notifications Best Practices for Apps (2026)

The push notification best practices that matter most: ask for permission only when the user sees why, send only messages a person would want even if you did not send them, cap how often you send, deep link straight to the thing the message is about, and clean up dead device tokens. Behind that you need a sending service; FCM and Expo's push service are free, OneSignal adds marketing tools, and a backend with built-in push (such as StitchDB) lets you send from your database when data changes.

#Part 1: the practices

#Ask at the right moment

On iOS, and on Android 13 and later, the app must ask before it may show notifications. You often get one good chance.

  • Do not ask on first launch. The user has no reason to say yes yet.
  • Ask after a moment that shows the value. Right after they follow a team, place an order or start a chat: "Want to know when your order ships?"
  • Use a soft prompt first. Show your own screen explaining what you will send, with Not now and Turn on. Only call the system prompt on Turn on. A user who taps Not now can be asked again later; a user who denies the system prompt may only be able to turn notifications on in Settings. Android's own guidance recommends asking in context, after a user action such as following someone or placing an order.
  • Respect the answer. If they say no, keep the app fully usable and offer a switch in settings.

#Send what people would ask for

Good push notifications are ones the user would want to hear about if they thought of it:

  • Transactional: order shipped, payment failed, booking confirmed, someone replied to you.
  • Time-sensitive: your ride is here, the auction ends in 10 minutes, a price you watch dropped.
  • Social: a direct message, a mention, a friend joined.

Weak ones:

  • "We miss you!" with nothing new.
  • Marketing blasts to everyone, every day.
  • Anything the user already saw inside the app.

#Make each message stand on its own

  • Lead with the specific thing. "Ada replied: 'Works for me, see you at 3'" beats "You have a new message".
  • Short title, useful body. Lock screens cut long text.
  • Deep link. Tapping should open the order, the chat, the item, not the home screen. Put the id in the data payload and route on it.
  • No sensitive content on the lock screen. One-time codes, health details or balances can be read by anyone holding the phone.

#Control frequency

  • Set a cap per user per day for anything that is not transactional.
  • Group bursts. Ten likes in a minute should be one notification: "Ada and 9 others liked your post".
  • Respect the user's time zone. Do not send non-urgent messages at 3 a.m. local time; schedule them.
  • Give choices. A settings page with categories (orders, messages, offers) keeps people from turning everything off.

#Keep an in-app inbox

A push notification is easy to swipe away. An inbox inside the app (a bell icon with an unread count) keeps the message around, works for users who declined push, and gives you somewhere to show history. Store notifications server-side so the inbox matches across devices.

#Treat device tokens as data you maintain

  • Register after sign-in, and again when the token changes. Tokens change after reinstalls and restores.
  • Unregister on sign-out. Otherwise the next person to sign in on that phone gets the previous user's notifications.
  • Remove tokens the push service says are invalid. APNs and FCM tell you when a token is no longer valid; keep sending to it and you waste quota and slow delivery.
  • One user, many devices. Store tokens per device, not one per user.

#Measure, then tune

Track sent, delivered, opened and, most importantly, opt-outs and uninstalls after a send. A campaign that gets opens but also turns off notifications for a share of users is a loss.

#Part 2: the best push notification services

You need something that talks to Apple Push Notification service (APNs) for iOS, Firebase Cloud Messaging (FCM) for Android, and Web Push for browsers. Options, with prices as of November 2026:

ServiceWhat it isPrice (as of November 2026)Best for
APNs and FCM directlyThe platform servicesFCM is listed as a no-cost Firebase productTeams that want no middleman
Expo push serviceOne API in front of APNs and FCM for Expo appsNo cost; up to 600 notifications per second per projectExpo and React Native apps
OneSignalMessaging platform with segments, campaigns, A/B testsFree plan for mobile push up to 1,000 MAU; Growth from $19/month plus $0.012 per mobile MAUMarketing-led teams
StitchDB pushPush built into a Postgres backend, with an in-app inboxIncluded: 10,000 notifications/month on Free, 1,000,000 on Pro, 5,000,000 on ScaleApps that send when their data changes

Sources: Firebase pricing, Expo push FAQ, OneSignal pricing.

#APNs and FCM directly

Pros: no extra vendor, full control over every field. Cons: two protocols, two sets of credentials, token cleanup, retries and your own queue. Pick this if you have a backend team and want minimal dependencies.

#Expo push service

Pros: one HTTP API and one token type for both platforms, a Node SDK, push tickets and receipts to check delivery, free. Cons: you still store tokens, batch requests (up to 100 per request), poll receipts and handle DeviceNotRegistered yourself, and it is for Expo apps. Push does not work in Expo Go from SDK 53; use a development build. See Expo push notifications backend.

#OneSignal

Pros: segmentation, campaign tools, journeys, analytics dashboards that marketing teams can use without engineers. Cons: priced per subscriber once you grow past the free plan, and your users' devices live in another system you sync with. Pick it when marketing campaigns are the main use.

#StitchDB push

StitchDB push notifications deliver to iPhones and iPads through APNs, Android through FCM, and browsers through Web Push. Sending runs on your project's server, and each message can also go into a per-user inbox table that updates live.

Setup is in Studio › Integrations › Push: Web Push works at once (StitchDB creates the key pair); for iOS upload your APNs .p8 key with Key ID, Team ID and bundle ID; for Android upload a Firebase service account JSON.

Register devices from the app:

JavaScript
await stitchdb.push.register({ platform: 'ios', token: apnsToken })
await stitchdb.push.register({ platform: 'android', token: fcmToken, topics: ['news'] })
await stitchdb.push.unregister({ platform: 'ios', token: apnsToken }) // on sign-out

Send from SQL, which means a trigger can notify the moment data changes:

SQL
create function notify_shipped() returns trigger
language plpgsql security definer set search_path = '' as $$
begin
  perform push.notify(new.user_id, 'Order shipped',
    'Order #' || new.id || ' is on its way', jsonb_build_object('order', new.id));
  return new;
end $$;

create trigger order_shipped after update of status on public.orders
  for each row when (new.status = 'shipped') execute function notify_shipped();

Or from your server with the service_role key, including scheduled sends for time zones:

JavaScript
await admin.push.send({
  user_id: userId,
  title: 'Your weekly summary',
  body: '3 new tasks were assigned to you',
  data: { screen: 'tasks' },
  send_at: nineAmLocal,   // optional: send later
})

The in-app inbox comes with it:

JavaScript
const { notifications, unread } = await stitchdb.push.inbox({ limit: 20 })
await stitchdb.push.markRead('all')
const stop = await stitchdb.push.onNotification((n) => showBadge(n))

Tokens that Apple, Google or the browser report as invalid are turned off automatically, and topics group devices for broadcasts.

Pros: no extra vendor, sends from triggers, inbox and token cleanup included, included in the plan. Cons: no marketing campaign builder or A/B testing; it takes native APNs and FCM tokens, not Expo push tokens; the platform is hosted in the EU only.

#Who should pick what

  • Marketing campaigns and segments: OneSignal.
  • An Expo app with a small backend: Expo's push service, or native tokens to a backend with push.
  • Push that follows your data (orders, messages, mentions): a backend with built-in push, such as StitchDB.
  • Full control and an existing backend team: APNs and FCM directly.

#FAQ

#How many push notifications per day is too many?

There is no safe universal number. Transactional messages can go out whenever they happen; for everything else, start with a low daily cap per user and watch opt-outs after each change.

#When should I ask for push permission?

After the user does something that makes the value obvious, with your own explanation screen first. Not on first launch.

#What is the best free push notification service?

FCM and Expo's push service have no cost. StitchDB includes 10,000 push notifications a month on its Free plan, with an inbox and SQL sending.

#Should I send push notifications from the client?

No. Sending needs secret credentials. Send from your server, an Edge Function or the database; on StitchDB, anon and authenticated users cannot send.

Send your first notification from SQL: read push notifications and create a free project. Building for iOS? See the best backend for iOS apps.