To add direct messages to an app you need four pieces, and the message bubbles are the easy one.
A direct message is one thread between two people, kept in order, with a rule on the server saying only those two can read it. To add direct messages to an app you build four things. A conversation record, a message list that updates while you watch it, a push notification for when the app is closed, and the controls deciding who may message whom. If you want many people in one room rather than two, that is adding chat, and the data model parts ways at the first line.
This page covers the thread record and the key that finds it, what a default backend does and does not encrypt, and how a message reaches a sleeping phone. It also covers when the right move is to skip the build entirely.
See what you are signing up forThe short version
Direct messages are cheap to build and expensive to keep.
The screens are a weekend. The plumbing is a week. What comes after never stops. Reports, blocks, deletion requests, one person who will not leave another alone, and a database full of private text that is now your responsibility.
Say the second part out loud before you start. A standard hosted backend protects traffic and stores rows your own server can read. That is not end to end encryption, and the difference is the first thing a user will assume you got right.
What you are actually building
Four pieces. A conversation record that names the two participants. An ordered list of messages hanging off it. A live connection so a new message appears without a pull to refresh. And a push notification, because nobody sits in your app waiting for one.
The fifth piece is not technical and it is the one that sets the real cost. Any in app messaging feature will attract someone who abuses it. You need a block, a report and a way to remove both a message and the account behind it. Ship without those and you will build them in a hurry, on a weekend, with an angry user waiting.
The toggles below sort the work into the part that is a weekend and the part that is a quarter. Read receipts and typing indicators look like polish and cost almost nothing. Attachments and end to end encryption are each their own project with their own failure modes.
Try it
What your messaging feature is really made of
Turn on what you intend to ship. The first three are the feature. The rest is where the weeks go.
A weekend of work
- Two person thread. A conversation record and an ordered message list.
- Live updates. One subscription on the thread that is open.
- Push when closed. Tokens, certificates and a send path on your server.
One thread, two people, and the key that finds it
The common mistake is storing messages as one flat list with a sender field and a recipient field, then querying both directions every time a screen opens. It is fine with a hundred messages and painful with a hundred thousand. Store the conversation first and hang the messages off it.
A one to one chat app needs a stable identity for each pair so both sides land on the same record. Sort the two user IDs, join them, and use the result as the conversation key. Alice opening a thread with Bob and Bob opening a thread with Alice now land on one conversation. No lookup step, and no pair of half threads each holding half the history.
Every message wants a server timestamp, a sender ID and a client ID you generate before sending. The client ID is what lets you show the message instantly and reconcile it when the server confirms. Order by the device clock instead and messages will shuffle the first time somebody's phone is a few seconds out. The wider shape of an in app messaging surface, including where the inbox sits and what a notification opens, follows from this one record.
Who can read it, and what you did not get for free
Access control for a private chat mobile app belongs on the server, never in the app. A determined person can read your bundle and call your API directly. The only check that counts is the one the backend makes before it hands back a row.
Hosted databases do this with rules you write once and deploy. Firestore, for example, evaluates every request from a client library against your security rules before reading or writing any data. So a rule saying only the two member IDs on a conversation may read its messages holds even when the app has been tampered with. That only works if the request carries a verified identity, which is why you add user login before you add messages rather than after.
Now the honest part. None of this is end to end encryption. Traffic is protected in transit and rows are stored under the operator's keys. Your server, your database console and anyone holding access to either can read every message sent. End to end encryption means the keys are generated on the two devices and the server only ever holds ciphertext. That is a different build: key exchange, key rotation, multi device sync, and an answer for the user who loses a phone and loses the history with it.
So do not promise privacy on the marketing page when what you have is a padlock on the connection. Pick the true sentence. Either messages are protected in transit and readable by us, or messages are end to end encrypted and unreadable by us. We have not verified the encryption terms of any particular backend for you, so read your provider's own documentation before that sentence goes live.
Getting a message to a phone that is asleep
A live listener only works while the app is open, which is almost never. Everything else is push, and push is a separate system with its own rules. Your server sends a notification request to Apple or Google, and the platform decides when the device sees it.
Two things surprise people. The first is size. Apple caps a remote notification payload at 4 KB, which is 4096 bytes, and at 5 KB for VoIP notifications. That is plenty for a name and a preview line, and nowhere near a conversation. The notification is a pointer. The app fetches the real thing when it opens. The second is exposure. Put the message text in the alert body and it travels through the push service. Then it lands on a lock screen, in front of whoever is holding the phone. That is a product decision, and it is why messengers offer a setting to hide previews.
Do not treat a push as proof of delivery either. Treat it as a nudge and treat the database as the truth. When the app opens it reloads the thread from the server, and any notification that never showed up stops mattering.
Five ways to give two people a thread
| Approach | Works when the app is closed | You hold the messages | Who can read them | What bites you later |
|---|---|---|---|---|
| Send people to SMS or WhatsApp | the other app handles it | No | outside your control | the conversation leaves your product |
| Inbox with no live updates | on next open | Yes | you can | it feels dead |
| Hosted chat service | Yes | a copy sits with the vendor | you and the vendor | the bill and the lock in |
| Your own backend plus push | Yes | Yes | you can | moderation and abuse reports |
| End to end encrypted build | Yes | Yes | only the two devices | lost phone means lost history |
Building the messaging side yourself
Most teams adding messages do not want a messenger. They want one thread bolted to something else: a buyer and a seller, a coach and a client, a tenant and a landlord. That context is the actual product, and it is the last thing a general purpose chat service will give you.
Newly is an AI app builder. You describe the app in plain English and it writes a real React Native and Expo project you own. It runs that on a cloud iPhone or Android simulator while it builds, then ships it to TestFlight and to Google Play internal testing. It is $25 a month and there is no free plan. It does not ship a backend of its own. The database behind your threads and the push service in front of them stay your choice, and the generated code is yours to point at whichever you pick. The code comes out as a ZIP from Settings, or through two way GitHub sync from the Deploy tab.
Before you write a single rule, get the model straight. The question of who can read a message is the one everything else hangs from. Answering it at the start is far cheaper than retrofitting it after the first support ticket.
Questions people ask about direct messages
Create a conversation record naming the two people and store messages under it with a server timestamp. Subscribe to that list so new messages arrive live, and send a push notification when the app is closed. Then add a block, a report and a delete path, because those are what the feature needs to survive real users.
Describe the thread your app actually needs
Write down who is allowed to message whom, and what happens the first time one of them reports the other, then build the messaging around those two answers.
Start building