Skip to main content

Offline Sending and Device Storage

Compatibility
This feature compatible with Better Messages 3.0.0 or higher

Every send goes through a queue on the member's own device before it goes to the server. A message typed in a tunnel, on a dropping hotel Wi-Fi, or a second before the browser is closed is kept and sent by itself once there is a connection again. The same local database keeps the conversations the member has opened, so the messenger draws from the device first and the network second.

Both work in the free version. Neither needs configuring — this page is mostly about what your members will see, and the two controls that exist for when something needs clearing.

The send queue#

A send does not wait for the network. The message appears in the conversation immediately and a job is written to the device's database. The job stays there until the server has acknowledged it. Closing the tab, reloading the page, or quitting the app does not lose it.

Everything the composer can produce is queued the same way: text messages, voice messages, video messages, stickers, GIFs and forwards. A forward to twelve conversations becomes twelve independent jobs — each one lands in its own conversation the moment it can, and each can be cancelled on its own.

What a member sees#

StateIn the conversation
Browser reports no networkThe composer shows You are offline"Messages you send now will go out once you are back online", or a count once some are waiting
Waiting for a connectionA clock where the delivery tick goes, labelled Waiting to send
Being sent right nowA spinner, labelled Sending…
The server refused itAn alert tick labelled Not sent, and the bubble grows a card reading Message not sent with the server's reason, plus Retry and Cancel

A message that simply goes through says nothing at all — no clock that turns into a tick a frame later. A send only starts announcing itself once it has something to report: bytes still going up, or an attempt that has already come back once.

Retry and cancel#

Queued and failed messages carry two actions in the message menu:

  • Send now (Retry once it has failed) — attempt it again immediately, without waiting out the backoff
  • Cancel sending — drop the job. Also sits directly on the message's hover strip, because it is the one thing a member is likely to want from a message that has not gone yet

What retries and what does not#

The queue distinguishes the connection failed from the server said no:

What came backWhat happens
No response at all, or a gateway answering for a backend that is not there (502, 503, 504)Treated as a connection problem. The job stays queued and is retried
Anything the site itself answered — a 403 refusal, a 500 from a plugin fatalTreated as a refusal. The job is parked as failed and only a manual retry sends it again

Retries back off: two seconds, then doubling, capped at one minute. Four things cut the wait short and restart the queue at once — the browser coming back online, the WebSocket reconnecting, the tab regaining focus, and the app returning to the foreground.

No duplicates

Every attempt carries the job's own id, so a message the server already stored but whose answer was lost on the way back is not stored twice — the server answers the repeat with the row it already has. This is also why a member can safely hit Retry on something whose fate is unclear.

Only one browser tab sends at a time, so two open tabs do not race each other through the same queue.

Storage on this device#

Conversations a member opens are kept on the device and are drawn from there on the next visit, so a conversation they read this morning opens without waiting for the network. The copy survives plugin updates and settings changes, which means on a busy site it would otherwise only ever grow — so it is kept to a budget.

Once a day, when the device's copy is over the limit, the oldest and least useful rows go:

  • Conversations beyond the newest 200, and untouched for 90 days. Never one that is pinned, unread, holds a draft, or has a message still waiting to be sent
  • Messages beyond the newest 200 per conversation on the web (500 in the app). Never a favorited or pinned one
  • People nobody left refers to, and the statuses of dropped conversations

Everything removed is a cache of what the server holds, and comes back the moment it is scrolled to. Drafts, the send queue, encryption keys and settings are never touched.

The default budget is 150 MB on the web and 1 GB in the mobile app. Both, and the row counts above, are filterable — see better_messages_local_db_policy.

The member's control#

The messenger's own settings panel — the gear at the bottom of the messenger — ends with Storage on this device, showing what this device is holding ("12.4 MB used · 3,180 messages in 46 conversations") and two buttons:

  • Free up space — runs the same trim immediately, and reports what went
  • Clear local data — drops the device's copy entirely. It asks first, and says plainly that drafts and messages still waiting to be sent go with it

The admin's control#

WP Admin → Better Messages → Settings → Tools → Local Database → Reset for all users bumps a generation number that every device checks on its next load. Each browser and app then drops the conversations it has cached and downloads them again.

Use it after a change that should make every device start over. It is not a routine button: unsent drafts on those devices are lost with the cache, though encryption keys are kept.

How to enable#

Nothing. The send queue and the device cache are how 3.0 sends and loads messages — there is no setting to turn either on, and no setting to turn them off.

Two related settings sit elsewhere:

  • Settings → Messaging → Conversation Features → Draft Messages — the per-conversation drafts described on Message drafts, stored beside the queue and likewise never trimmed
  • Local browser encryption — encrypts everything the device holds with AES-256, for kiosks and shared computers (WebSocket version)

Adjusting the budget#

add_filter( 'better_messages_local_db_policy', function( $policy ) {
$policy['limitWeb'] = 50 * 1024 * 1024; // 50 MB in a browser
$policy['limitApp'] = 512 * 1024 * 1024; // 512 MB in the app
$policy['messagesPerThreadWeb'] = 100;
$policy['messagesPerThreadApp'] = 300;
$policy['threadsKeep'] = 100;
$policy['threadsMaxAgeDays'] = 30;

return $policy;
} );

Frequently asked questions#

Can members read old conversations with no connection at all?#

They can read whatever this device has already downloaded, and they can write — the messages wait in the queue. What they cannot do is load a conversation they have never opened on this device, or scroll back past what was cached, because neither exists locally to read.

Does the queue work in the free version?#

Yes. The queue, the offline notice, the clock and spinner ticks and the retry card are all in every build — they describe this browser's own send, not the recipient's receipt. What the free version does not draw is the delivery tick after that (sent / delivered / seen), which is a WebSocket-version feature.

What happens to a queued message if the member logs out, or the site clears local data?#

It goes. The queue lives in the device's own database under that member's id: Clear local data in their settings, an admin Reset for all users, or the browser clearing site data all remove messages that had not been sent yet. Nothing is lost on the server side, because nothing had reached it.

Do queued messages keep the time they were typed?#

Only if they go out within a minute of it. Each send offers the server the moment it was composed, and the server accepts it when it is within 60 seconds of its own clock — which covers a slow request or a brief drop, but not a message that waited out a tunnel. Anything queued longer than that is stored at the time it actually arrived, so a conversation never ends up with messages claiming to predate the replies above them.

Are attachments queued too?#

Yes. Files staged in the composer ride along with the job and are uploaded when it runs, so a photo picked while offline is not lost. A job carrying files is the one case that announces itself from the very first frame, since there are visibly bytes to move.

How is this different from message drafts?#

A draft is text the member has not sent. A queued message is one they have sent — it is in the conversation, it will go out by itself, and they would have to cancel it to stop it. Drafts are covered on Message drafts.

Does clearing local data delete anything from the server?#

No. Everything the device holds is a copy. The only thing that exists nowhere else is a message still waiting in the queue.

See also#