Offline-First Is Not a Feature

Most applications are built with one assumption:
The internet is available.
The UI loads data from an API. The user submits a form. The server saves it. The application moves on.
That works well when connectivity is reliable.
But what happens when it isn't?
A clinic loses its connection. A field worker has no signal. A power outage takes the network down. A small business is working from an area with inconsistent connectivity.
The user's work still needs to continue.
That is where offline-first architecture becomes important.
Offline-first starts before the first route or component
I don't think of offline-first as a feature that gets added after an application is already built.
It is an architectural decision.
The difference matters because offline-first changes how you think about:
Data storage
Application state
Reads and writes
Synchronization
Failed requests
Conflicts
Deletes
Network availability
If the application is designed around a remote API from the beginning, adding offline support later can become a significant architectural change.
With offline-first, the local data store becomes part of the application's normal operation.
The application should be able to do its core job whether the network is available or not.
Offline mode vs offline-first
These two ideas are often used interchangeably, but they are different.
Offline mode
Offline mode usually means the application has an online-first architecture with a fallback.
The application detects that the connection is gone and might:
Show cached data
Disable certain features
Display an offline message
Wait for the connection to return
The network is still the default assumption.
Offline-first
With offline-first, local storage is part of the normal architecture.
The application reads and writes locally first.
Synchronization with the server happens separately, usually in the background.
The basic flow becomes:
User → Local database → UI
and then:
Local database → Sync queue → Server
The important difference is that the user's core action does not have to wait for the network.
How I'm using this in Aafiyat
Aafiyat is a clinic management system I'm building for private practices.
One of the questions I asked while designing it was simple:
What happens if the clinic loses its internet connection in the middle of the working day?
A doctor shouldn't have to stop because the cloud is temporarily unavailable.
Aafiyat is built as an Electron application with SQLite as the local database.
Patient records, consultations, prescriptions and billing data are written locally first.
The application does not make a network request part of the critical save path.
The basic flow is:
Generate a UUID for the record.
Open the local database.
Insert the record into SQLite.
Notify the application that the local change happened.
Add a synchronization job to the sync queue.
Synchronize with Supabase when connectivity is available.
The important part is step three.
The user's data is saved before we worry about the network.
The UI can confirm the action immediately instead of waiting for an API response.
The sync queue
Saving locally is only half of the problem.
Eventually, the local data needs to reach the server.
That's where the synchronization queue comes in.
Aafiyat keeps pending synchronization jobs in a SQLite sync_queue.
A simplified flow looks like this:
User creates record
↓
SQLite
↓
sync_queue
↓
Internet available?
↓
┌────┴────┐
│ │
Yes No
│ │
Sync Stay queued
│
Supabase
If synchronization fails, the item stays in the queue and can be retried later.
This is one of the reasons offline-first applications require more planning than ordinary CRUD applications.
You aren't only building:
Create → API → Database
You're building something closer to:
Create
↓
Local storage
↓
Queue
↓
Retry
↓
Synchronize
↓
Resolve conflicts
The Nankana Home Care PWA
I'm using a similar idea in the Nankana Home Care project.
The platform includes an offline-first PWA for medical assistants who may need to record patient information without an internet connection.
The local data is stored using IndexedDB.
When connectivity becomes available, a synchronization worker processes the queued information and sends it to Supabase.
For example, patient data can be queued locally and synchronized later.
If a request fails, it isn't simply discarded.
The item remains queued so the application has another opportunity to synchronize it.
The architecture is different from Aafiyat because the technology is different, but the underlying idea is the same:
Local work first. Cloud synchronization second.
Synchronization is the hard part
This is where offline-first becomes much more interesting.
What happens when the same record changes in two places?
For example:
Local device
Patient name → "Muhammad Ali"
↓
Server
Patient name → "Muhammad A."
Which version should win?
There isn't one universal answer.
Possible strategies include:
Last-write-wins
Server-wins
Client-wins
Manual merging
Domain-specific conflict resolution
For Aafiyat, I'm using a last-write-wins approach based on timestamps.
The most recent updated_at value determines which version is accepted.
That's a deliberate trade-off based on the workflow I'm building for.
The important thing isn't that one strategy is always correct.
The important thing is to decide what should happen before conflicts occur in production.
Deletes are also harder
Updates aren't the only problem.
Consider a record deleted while the device is offline.
The application needs to remember that deletion and eventually communicate it to the server.
Otherwise, synchronization could accidentally recreate the record.
Offline-first means thinking about the entire lifecycle of data:
Create
Read
Update
Delete
Sync
Retry
Conflict
Not just the happy path.
Do you actually need offline-first?
No.
Not every application needs this architecture.
A simple marketing website probably doesn't need a local database and synchronization queue.
A basic landing page doesn't need to continue operating without connectivity.
But the situation changes when the user's core job depends on the application.
Think about:
Clinics
Pharmacies
Field workers
POS systems
Local businesses
Applications used in areas with unreliable connectivity
In these environments, connectivity can become an operational constraint rather than a minor inconvenience.
That's when offline-first starts making sense.
The trade-off
Offline-first isn't free.
It adds complexity.
You now have to think about:
Local storage
Two sources of data
Synchronization
Retry mechanisms
Conflicts
Storage limits
Deletes
Background processing
The initial architecture takes longer.
But the benefit is that the application doesn't have to stop doing its core job every time the network disappears.
For the right environment, that trade-off is worth considering.
Build for the environment your users actually have
This is probably the biggest lesson I've taken from building these systems.
It's easy to design software around an ideal environment:
Fast internet. Modern hardware. Stable power. Constant connectivity.
Real users don't always have that environment.
In the applications I'm building, I want the architecture to reflect that reality.
The internet is useful.
The cloud is useful.
But neither should automatically become a requirement for every core action.
Offline-first isn't a feature you add at the end.
It's a decision you make when you design the application.
And sometimes, that decision determines whether the software keeps working when everything around it doesn't.
