TL;DR
- Push is the surface users see 20x a week — it belongs in the design system, not in a marketing sprint.
- 12 notification archetypes cover almost every app: social-mention, social-activity, DM, group-message, transaction-confirm, transaction-update, reminder-user-set, reminder-streak, security-alert, content-drop, system-status, promotional.
- 3 states get skipped every time: grouped, expanded, and the lock-screen/Notification-Center/banner size variants.
- The handoff artifact is a YAML file per component that maps 1:1 to an
expo-notificationspayload. If code diverges from the YAML, code review fails. - A weekly 30-minute notification review catches copy drift before users do.
Most design systems stop at the app icon. Push notifications live outside the app, on the lock screen, in the notification shade, in the app-switcher preview — and they get designed ad-hoc, by whoever is writing the marketing send that week. Then a user sees seven notifications from your brand and none of them look like they came from the same company.
Six months ago I built a push-notification design system for a React Native app the team was scaling from 20k to 200k users. Twelve components, three states each, one Figma file, and a handoff spec that maps every component to a specific expo-notifications payload shape. Here's the whole thing.
Why Push Notifications Belong in the Design System
Push is the most visible surface of your product for users who aren't in the app. If your in-app UI is polished but your notifications look like they were written by a bot, the notification is what users remember — it's the surface they see 20 times a week, versus the in-app UI they see three times.
When push isn't in the design system, three things break:
- Copy voice drifts. Marketing writes chirpy, engineering writes terse, and the same user gets both.
- Icon and color usage is inconsistent. Some notifications use the brand accent, some use system defaults, some use whatever emoji the sender liked.
- Rich media (images, actions, replies) get added case-by-case, with no consistent affordance for users to know what interactions are available.
A design system fixes all three by making the notification a first-class component with reviewable specs, not an afterthought at the end of a marketing sprint.
The 12 Components
After working across four apps, the same twelve notification archetypes keep showing up. Design one component per archetype:
- Social-mention — someone tagged/mentioned/replied to the user.
- Social-activity — someone the user follows did something.
- Direct-message — a 1:1 message from another user.
- Group-message — a message in a group, showing group name.
- Transaction-confirm — order placed, payment received.
- Transaction-update — delivery moved, ride arrived, booking changed.
- Reminder-user-set — the alarm/reminder the user configured.
- Reminder-streak — 'you're one workout from your streak' style.
- Security-alert — new login, fraud check, 2FA code.
- Content-drop — new episode, new article from a followed source.
- System-status — app update available, service outage.
- Promotional — offer, discount, event (use sparingly).
Each component gets its own frame in Figma with a filled-in example, a spec sheet, and links to the two engineers who own the code path that sends it.
The Three States Designers Forget
Designers default to designing the single-notification-on-lock-screen state. Then production shows three other states that were never designed:
Grouped state (iOS + Android). When your app sends 3+ notifications in a session, the OS collapses them into a stack. The stack header is 'YourApp' — but if you set threadIdentifier (iOS) or channelId + group (Android), you get semantic grouping like '4 messages from Sarah.' Design what those group headers look like. Design what happens at 15 notifications (do they collapse further?). Design the tap-to-expand animation.
Expanded state. Long-press on iOS or swipe-down on Android reveals the expanded notification, which can show images, quick-reply fields, action buttons. Most apps ship the default (a bigger version of the collapsed view). You can do better: this is prime real estate for a mini-interaction that doesn't require opening the app.
Lock-screen preview vs Notification Center vs Banner. Same content, three different sizing constraints. The banner (top of screen when phone is unlocked) truncates at ~40 chars. The lock-screen preview shows 2-3 lines. Notification Center shows 4-5 lines and stacks. Design the copy for the tightest constraint first, then progressively enhance for the roomier surfaces.
Figma-to-Code Handoff That Actually Survives
The handoff spec I ship looks like this — one file per component, machine-readable:
component: social-mention
trigger: user_mentioned_in_comment
payload:
title: "{{actor.display_name}} mentioned you"
body: "{{excerpt}}"
data:
deep_link: "app://comments/{{comment_id}}"
actor_id: "{{actor.id}}"
thread_id: "{{parent_id}}"
ios:
category_id: SOCIAL_MENTION
thread_identifier: "mentions_{{parent_id}}"
interruption_level: active
relevance_score: 0.7
android:
channel_id: social_mentions
group: mentions
priority: default
copy_rules:
- excerpt max 60 chars, ellipsize
- never include reactor emojis in title
- if actor is verified, prepend U+2713
The React Native code that sends it maps 1:1:
import * as Notifications from 'expo-notifications';
async function sendMentionNotification(params: {
actorName: string;
actorVerified: boolean;
excerpt: string;
commentId: string;
parentId: string;
}) {
const title = params.actorVerified
? `\u2713 ${params.actorName} mentioned you`
: `${params.actorName} mentioned you`;
return Notifications.scheduleNotificationAsync({
content: {
title,
body: truncate(params.excerpt, 60),
categoryIdentifier: 'SOCIAL_MENTION',
data: {
deep_link: `app://comments/${params.commentId}`,
actor_id: params.actorName,
thread_id: params.parentId,
},
},
trigger: null,
});
}
The yaml file is the contract. If the code diverges from the yaml, code review fails. If a designer wants to change the notification, they change the yaml, which triggers a code PR. Everyone's working from the same spec, and copy can't drift silently.
Wiring the channels, categories, and deep-link routing by hand is the part that eats a sprint — the RapidNative starters ship with the expo-notifications scaffolding and channel config already in place, so you can go straight to writing the specs.
The Weekly Notification Design Review
Even with a design system, notification copy drifts. Marketing writes a one-off. Engineering hardcodes a fallback. QA never sees it because notifications only fire in production. Six months in, you have 40 slightly-off notifications your team can't remember shipping.
Run a weekly 30-minute notification design review. The agenda:
- Screenshot every notification the app sent in the last 7 days (via the log I mentioned above — instrument every send).
- Compare each against its yaml spec. Flag drift.
- Look at delivery + open rates per component. Retire any component with under 5% open rate for two weeks running.
- Discuss the copy of any new components proposed this week.
Thirty minutes, weekly. It's the single highest-leverage design meeting I've ever run — small enough that engineering shows up, focused enough that decisions actually get made, and it catches the drift before users notice.
When we ship this notification design system in RapidNative starters, teams stop treating push as marketing exhaust and start treating it as a product surface. The difference shows up in retention within a quarter.
How many of the 12 archetypes does your app actually send — and how many of them were designed on purpose? Drop a comment with the notification you know is drifting.
Top comments (0)