Customer.io notification inbox vs building your own in React

By·Published·Updated
Customer.io notification inbox vs building your own in React

Between 1768 and 1774, the Swiss clockmaker Pierre Jaquet-Droz, his son Henri-Louis and Jean-Frédéric Leschot built three automata. One has a mechanism of 6,000 metal parts (History of Information). The most famous is the Writer, a seated boy who dips a real quill in real ink. He writes any sentence of up to 40 characters, set by tabs that control the cams that move the pen (Linda Hall Library).

He still works, in the Museum of Art and History in Neuchâtel. Nobody in the 1770s needed him to get a letter written, though. A clerk with a pen did that. The Writer exists because the machine itself was the point.

That is the whole question behind building your own notification inbox in React. If the inbox is what your customers pay for, build the machine. If you need messages to land somewhere people can find them later, use the pen.

TL;DR: Build your own notification inbox only when the inbox is part of the product. For everyone else, Customer.io's hosted inbox wins. It already owns what home-built inboxes keep getting wrong: read state stored with each message, deletes that clear on every device, badge counts that match the list, and push copies for automations and API-triggered broadcasts. You also do not have to give up your own React components. Customer.io's build-your-own mode hands your code the messages as JSON while it keeps storage, state, expiry and metrics. On our own arithmetic, a fully home-built inbox for web and mobile is about 40 engineer-days to first release, against about 15 for your own React UI on Customer.io's inbox. The reasons to build are real but narrow: messages expire within 60 days, there is one inbox per workspace, and everything in the feed must be sent through Customer.io.

What does Customer.io's notification inbox give you?

A message feed that persists inside your website or app. Each message stays until the person removes it or it expires, up to 60 days after sending (set up your notification inbox). It comes in two modes, and the difference matters more than anything else here.

The visual inbox. You create it in workspace settings, style it in Design Studio (icon, colours, unread badge, position, dark mode) and publish it. On the web, the JavaScript snippet renders it with no front-end code; the imported cdp-analytics-browser package needs version 0.4.16 or later. On mobile, the SDKs ship native views. In React Native, NotificationInboxOverlayView is the whole integration. It shows a bell with an unread badge, slides out the list, marks messages opened and reports opens and clicks (React Native inbox).

Build your own. Customer.io delivers each message as a JSON payload and your code renders it. On the web, analytics.inbox() gives you messages(), total(), totalUnopened() and onUpdates(), plus per-message markOpened(), markUnopened(), markDeleted() and trackClick() (display inbox messages). Minimum versions are 0.3.11 for the browser package, iOS 4.2 and Android 4.16. React Native needs 6.2, Flutter 3.3 and Expo 3.1 (build your own inbox).

Either way, you send from an automation, a broadcast, or your backend through POST https://api.customer.io/v1/send/inbox_message (trigger inbox messages from your backend).

Why so many teams think they have to build

Release order. The 19 February 2026 release was build-your-own only: "For now, you'll build your notification inbox yourself" (the February release note). The no-code website inbox followed on 23 June 2026, and push copies in the inbox on 22 July 2026.

Three ways to put an inbox in a React app

Build it in React usually means one of three projects, and teams argue because they picture different ones.

Option Who renders Who stores messages and state Front-end work Best for
Visual inbox Customer.io Customer.io None on web; mount a component on mobile Most teams
Your React UI on Customer.io's inbox You, from JSON Customer.io Your components, wired to inbox() An exact design-system match
Fully home-built You You Everything, front and back Inboxes that are part of the product

You control the markup; Customer.io holds the messages, opened flag, expiry and metrics. Marketers still compose in automations and broadcasts, in the advanced editor (compose JSON messages).

The price is a contract: everyone who sends needs to know which payloads, types and topics your client expects (build your own inbox). Write it down before the first send.

Setup steps are in our no-code notification inbox walkthrough; the channel choice is in notification inbox vs push.

What a home-built React inbox has to implement

A React list with a bell icon is a weekend. An inbox is not. The estimates below are our own arithmetic, not a measurement. They assume one experienced engineer who works across back end and front end, an existing React web app and React Native app, existing auth, and no real-time infrastructure yet.

Piece What it involves Home-built (engineer-days) React UI on Customer.io (engineer-days)
Storage A message table per recipient, state columns, indexes, retention 3 0
Send path An endpoint, templating, personalisation, idempotency 3 0
Real-time delivery WebSocket or server-sent events, reconnects, fallback polling 5 0
Read state Opened, unopened and deleted per person, server-side for every device 3 0
Badge counts An unread count that matches the list on every tab and device 2 0
Pagination and filters Cursors, topics, empty states 2 1
Expiry Per-message expiry and a clean-up job 1 0
Auth Per-user access to the feed and the live connection 2 0
Deep links Routing message actions on web and mobile 2 2
Analytics Sent, opened and clicked, sent back to Customer.io for segments 3 1
Web UI Bell, panel, accessibility, dark mode 4 4
Mobile UI The same in React Native 5 5
Push copy Writing each push into the feed, including when push fails 2 1*
QA Multi-device, multi-tab and load testing 3 1
Total 40 15

*The day is for testing copied pushes in your renderer, because the docs do not describe their payload for build-your-own clients.

The totals are column sums. Your rows will differ, but the shape holds: seven of the fourteen rows cost nothing on the middle path.

Budget even 2 engineer-days a month for upkeep and that is 24 a year.

Where home-built inboxes keep going wrong

Read state that disagrees between devices

The demo keeps "read" in component state or local storage. Then someone reads a message on their phone and the web badge still says 3.

Customer.io keeps state with the message. Each payload carries an opened field, and markOpened() reports the message as delivered and opened (display inbox messages). For deletes, the docs are explicit: a dismissed message is removed from the person's inbox on every device (iOS inbox). Opens are not described with that wording, so check opened state on two devices before you promise it.

Badge counts that drift from the list

The classic bug: the list refetches, the count does not, and the bell shows 2 over an empty panel.

On the middle path you call totalUnopened() after each state change, as Customer.io's sample code does. On the visual inbox, opening the panel marks visible messages opened and the badge follows (React Native inbox). An icon badge on an installed web app is shakier: MDN marks setAppBadge() as limited availability and secure-context only (MDN).

Real time that breaks with a second tab

Home-built inboxes often pick server-sent events, because the feed flows one way. MDN's warning bites here. Without HTTP/2, browsers cap SSE at 6 connections per browser and domain, across all tabs. Chrome and Firefox have marked that "Won't fix" (MDN, EventSource). WebSockets are not subject to that SSE cap, but the standard WebSocket interface has no backpressure (MDN, WebSocket API).

Customer.io has made those choices for you. Identified website visitors get messages in real time while you have active in-app messages. Otherwise the SDK polls every 10 seconds with a message queued, every 60 seconds without one, and every 180 seconds before your first send. Mobile real time is still rolling out and is enabled on request (in-app getting started). Inbox messages follow similar delivery patterns (inbox message metrics).

That lag is invisible in an inbox and wrong for chat, one honest reason to build.

Metrics that never reach your segments

A home-built inbox records opens in your database, and your Customer.io segments never see them unless you send each one back as an event.

The hosted inbox reports sent, delivered, opened, clicked and failed, and the visual inbox reports opens, clicks and deletes by itself (inbox message metrics). One catch: delivered and opened arrive together, when the person sees the message. It is the trap we covered in in-app opened means displayed: read the open rate as "seen".

Why the push copy is hard to copy

Since 22 July 2026, a push can drop a copy into the inbox, using its title and message and expiring 60 days after sending. The workflow sends the copy even when it cannot deliver the push, for example when the person has no device that can receive it (send push notifications).

That is one setting on the push. Home-built, it is a hook in your push pipeline that still fires when the provider rejects the token. Teams build the happy path and miss that failure path, which is the one the feature exists for.

The constraints the announcement leaves out:

  • Only pushes in an automation or an API-triggered broadcast. One-time and transactional pushes cannot be added.
  • Only a standard rich push, not a custom payload, and only to a published inbox (send push notifications).
  • The visual inbox serves only your default app and your website. A push to any other app gets no inbox copy (set up your notification inbox).
  • The copy is its own delivery, named (Notification Inbox) plus the push's name.

The docs do not describe the payload a build-your-own client receives for a copied push. On the middle path, send a test copy and check your renderer handles it.

When building your own is the right call

Build when one of these is true.

The inbox is a product feature. A marketplace's order centre, a collaboration tool's mentions and approvals, a fintech's statements feed. If customers judge the product partly by the inbox, you will want threads, actions and states a marketing inbox does not model.

Messages must outlive 60 days. Every Customer.io inbox message expires within 60 days. A Liquid expiry left unset falls back to 30 days (send inbox messages). An inbox that doubles as account history needs your own storage.

One feed across several sources. You get one notification inbox per workspace (set up your notification inbox). Your backend can send to it through the API, but another vendor's alerts cannot appear in it.

The data must stay on infrastructure you control. If a contract or regulator says message content cannot sit with a third party, that settles it before engineering starts.

Not reasons to build: your own components, design system or animations, which the middle path covers. Several mobile apps in one workspace is not one either. The visual inbox serves only the default app, but the build-your-own path can route messages to each app by topic (build your own inbox). Our post on multiple apps in one workspace covers the routing.

When the hosted inbox wins

The visual inbox wins when nobody wants to own front-end code for it. A marketer can create, style and publish it, and style changes reach mobile apps on the SDK's next refresh with no app update (React Native inbox). The middle path wins when design must match exactly.

Customer.io's billing page charges overages on profiles and emails (how we bill), and inbox messages are not a line item. The plan features page lists collections, dedicated IPs and anonymous in-app messages as messaging features, not the inbox (plan features). Confirm on your own billing page.

The trade-offs you accept:

  • No pagination in the API. The documented inbox() call takes topic filters only (JavaScript inbox). The 60-day ceiling keeps lists bounded; heavy senders should page in the UI.
  • The docs disagree on expiry. The setup page says every message expires "up to 60 days after you send it". The advanced editor page says messages "only expire if they're sent but not delivered" (compose JSON messages). Plan for 60 days as the ceiling.
  • Debugging. Our in-app message not showing checklist applies to inbox messages too.
  • iOS versions. The overlay needs iOS 16 or later; the bell and list need iOS 13 (iOS inbox).

Five questions that decide it

Question If yes If no
Do customers judge the product partly by its inbox? Build Next question
Must messages live longer than 60 days? Build your own storage Next question
Must the feed show items that never pass through Customer.io? Build the feed Next question
Must message content stay on your own infrastructure? Build Next question
Must it match your design system exactly? React UI on Customer.io's inbox Visual inbox

Five noes means the visual inbox. A yes to only the last question means your own React UI on Customer.io's inbox.

Want a second pair of eyes on the decision, or help writing the payload contract between marketing and engineering? Send us the details.

Frequently asked questions

How hard is it to build a React notification inbox versus using a hosted in-app notification SDK?

On our own arithmetic, a fully home-built inbox for web and React Native is about 40 engineer-days to first release, against about 15 for your own React UI on Customer.io's inbox. The gap is storage, real-time delivery, read state, badges, expiry and auth, which Customer.io's inbox already handles.

Can I use my own React components with Customer.io's notification inbox?

Yes, with the build-your-own approach. Your components render each message's JSON through the inbox() methods, while Customer.io keeps storage, opened state, expiry and metrics.

Does Customer.io's notification inbox sync across devices?

Deleted messages do: when a person dismisses a message, the SDK removes it from their inbox on every device. Opened state comes back from Customer.io in each message's opened field, so test opens on two devices before relying on it.

How long do Customer.io inbox messages last?

Up to 60 days after you send them. That is both the default and the maximum, a Liquid expiry left unset falls back to 30 days, and the docs disagree on whether delivered messages expire, so plan for 60 days.

Can I store a copy of a push notification in the Customer.io inbox?

Yes, since 22 July 2026, for pushes in an automation or an API-triggered broadcast. The push must be a standard rich push to your default app, the inbox must be published, and the copy is sent even when the push cannot be delivered.

Does Customer.io's notification inbox work in React Native?

Yes, the visual inbox ships in the customerio-reactnative package. Mounting NotificationInboxOverlayView near the root of your app is the whole integration, badge and metrics included.

When should I build my own notification inbox instead of using Customer.io's?

Build when the inbox is part of the product, when messages must outlive 60 days, when the feed needs items Customer.io never sends, or when content must stay on your infrastructure.

Does the Customer.io notification inbox cost extra?

Customer.io's billing page lists overage charges for profiles and emails, and the inbox is not among them. The plan features page does not list the inbox either, so confirm on your own billing page.

Sources

Want this working in your Customer.io workspace?

It's what we do all day for SMBs—strategy, automations, deliverability and hands-on execution.

See how we work as a Customer.io agency →
David Crowther
Book a free consultation →

On our call, you'll be speaking with David Crowther, founder of NerveCentral.

Our initial consultation is not a sales call—you'll talk, I'll listen and ask questions—then I'll come back to you within 48 hours with our best ideas on how to grow your business.