DEV Community

Satavisha Dutta
Satavisha Dutta

Posted on

Progressive Web Apps: How to Build Offline-First Web Applications

A web application usually assumes that the user has a reliable internet connection.
That assumption is easy to make when developing on a fast connection with modern hardware. But real users may experience weak Wi-Fi, unstable mobile networks, temporary outages, or no connectivity at all.
For some applications, a short network interruption is merely inconvenient. For others, it can prevent users from completing important tasks.
This is where Progressive Web Apps (PWAs) and offline-first development become useful.
A PWA is still a web application, but it can use additional web platform capabilities to provide experiences that feel more like installed applications. A web app manifest provides information needed for installation, while service workers can support offline and background functionality.
For developers building skills across frontend and backend technologies, APIs, databases, and deployment, PWAs are an interesting full-stack project because they require thinking about what happens when the network works—and what happens when it doesn't. Developers looking for a structured way to build broader web-development skills can also explore the Eduonix Lifetime Full Stack Web Development Membership alongside practical projects.

What Is an Offline-First Web Application?

An offline-first application is designed with unreliable connectivity in mind.
Instead of assuming:

The network is always available.

the application is designed around:

The network may disappear, so the application should degrade gracefully.

This does not necessarily mean that the entire application must work without the internet.
A better approach is to identify which functionality can reasonably work offline.
For example, a note-taking application might allow users to:

  • Open previously loaded notes
  • Create new notes
  • Edit existing notes
  • Save changes locally

When connectivity returns, the application can synchronize those changes with the server.
A banking application, on the other hand, should not pretend that sensitive transactions have been completed when the server cannot confirm them.
The important principle is:
Offline support should match the application's data and business requirements.

The Role of Service Workers

A service worker is a background script that operates separately from the main web page.
One of its most important capabilities is intercepting network requests and deciding how the application should respond.
For example:
Browser
   |
   v
Service Worker
   |
   +----> Cache
   |
   +----> Network

When the browser requests a resource, the service worker can potentially return a cached version, request the resource from the network, or combine both approaches.
MDN describes service workers as a mechanism that can intercept requests and provide cached resources, enabling applications to offer an offline-first experience.
This creates an important architectural layer between the application and the network.
Instead of every request going directly from:
Application → Internet

some requests can follow:
Application → Service Worker → Cache
                         \
                          → Network

That flexibility is one of the foundations of offline web applications.

What Should You Cache?

Caching everything is not a good strategy.
Some resources are relatively stable:

  • CSS files
  • JavaScript bundles
  • Application shell
  • Icons
  • Fonts
  • Static images

Other information changes frequently:

  • User profiles
  • Inventory
  • Account balances
  • Notifications
  • Live dashboards
  • Search results

MDN points out that caching can improve both offline operation and responsiveness, but also notes that freshness becomes an important trade-off. Some resources should not simply be treated like static assets.
This means developers need a caching strategy rather than simply adding a service worker and caching everything.

Common Caching Strategies

Different resources can use different strategies.

Cache First

The application checks the cache first.
Request
   |
Cache available?
  / \
Yes  No
 |    |
Cache Network

This can work well for relatively stable assets.
The advantage is speed and offline availability.
The disadvantage is that users may receive outdated information if the cache is not updated appropriately.

Network First

The application tries the network first and uses cached content if the network request fails.
This can be useful when fresh information is more important than immediate availability.
Request
   |
Network
 /     \
Success Failure
  |       |
Response  Cache

This approach can be appropriate for content that changes regularly but still benefits from an offline fallback.

Stale While Revalidate

Another approach is to return cached content quickly while requesting a newer version from the network in the background.
Conceptually:
Request
   |
Return cached version
   |
Fetch newer version
   |
Update cache

This can provide a fast interface while still allowing cached content to become fresher.
There is no universal caching strategy. The correct choice depends on what the resource represents and how damaging stale information would be.

The Web App Manifest

Caching is only one part of the PWA experience.
A web app manifest is a JSON file that provides information about how a web application should appear and behave when installed.
It can describe information such as:

  • Application name
  • Short name
  • Icons
  • Start URL
  • Display mode
  • Theme color
  • Shortcuts

MDN describes the manifest as a JSON resource containing information that browsers can use when installing and integrating a web application.
A simplified example looks like:
{
  "name": "Field Notes",
  "short_name": "Notes",
  "start_url": "/",
  "display": "standalone",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ]
}

The manifest does not magically turn a website into a native application.
Instead, it gives the browser information about the web application and its intended installed experience.
For installability, browsers have specific requirements. For example, MDN notes that Chromium-based browsers require certain manifest information and that installable PWAs must be served over HTTPS, with localhost and loopback addresses allowed during development.

Offline UX Is More Than Caching

A common mistake is to think that offline support simply means displaying cached pages.
The user experience matters just as much.
Suppose a user creates a note while offline.
If the interface gives no indication of whether the note was saved, the user may assume the data was lost.
A better interface might communicate:
Saved locally
Waiting for connection
Syncing...
Synced

This creates a clear mental model for the user.
Offline applications should therefore consider states such as:

  • Online
  • Offline
  • Saving locally
  • Waiting for synchronization
  • Synchronizing
  • Synchronization failed
  • Successfully synchronized

The interface should also avoid claiming that something has been completed on the server when it has only been stored locally.
That distinction becomes particularly important when data has business consequences.

Local Storage and IndexedDB

Offline applications often need local data storage.
For very small pieces of information, browser storage mechanisms can sometimes be sufficient.
More complex applications may use IndexedDB, which is designed for storing larger amounts of structured data on the client side. MDN lists IndexedDB among the technologies that can support richer PWA functionality and offline data storage.
For example, an offline note-taking application could store:
Note ID
Title
Content
Last modified time
Sync status

The local database then becomes part of the application's offline architecture.
This introduces another important consideration:
Local data is not automatically the same as server data.
Developers need to decide how and when the two states should be synchronized.

Designing Synchronization

Synchronization becomes one of the more interesting parts of offline-first development.
Imagine this scenario:

  1. A user opens a task-management application.
  2. The network disconnects.
  3. The user marks three tasks as completed.
  4. The changes are stored locally.
  5. The network becomes available again.
  6. The application sends the changes to the server.

A simple system might process these changes sequentially.
But real applications can become more complicated.
What happens if another device changed the same task?
For example:
Device A: Task → Completed

Device B: Task → In Progress

Both changes may have happened before synchronization.
The application needs a strategy for resolving the conflict.
Possible approaches include:

  • Last-write-wins
  • Version numbers
  • Timestamps
  • Server-side conflict resolution
  • User-assisted conflict resolution
  • Domain-specific rules

There is no universal solution.
A note-taking application may use one strategy, while collaborative software may require something much more sophisticated.
This is why offline development becomes a valuable full-stack learning exercise: it forces developers to think about data consistency rather than just interface behavior.

Background Synchronization

Service workers can also participate in background operations.
MDN's PWA documentation lists technologies such as Background Sync and Background Fetch as mechanisms that can support work that should happen when connectivity becomes available or when larger resources need to be downloaded.
Imagine a field application used by workers who regularly lose connectivity.
Instead of forcing every action to wait for an internet connection, the application could store appropriate work locally and synchronize it when a stable connection becomes available.
The key word is appropriate.
Not every task should be queued automatically.
Developers need to understand the business consequences of delayed operations before introducing background synchronization.

Updating Cached Applications

Caching introduces an important problem:
How do you update old content?
Suppose version 1 of an application caches:
app-v1.js
style-v1.css

Later, version 2 is deployed.
If the service worker continues serving old resources, users could end up with a mixture of old and new application files.
Service workers therefore need an update strategy.
MDN's service worker guidance describes installation, activation, cache versioning, updating, and removal of old cached resources as important parts of service worker management.
The W3C specification also makes clear that cache contents are managed by the application and do not automatically become correct simply because a service worker changes. Developers need to manage cache versions and updates deliberately.
A basic approach is to use versioned cache names:
app-cache-v1
app-cache-v2

When the new version becomes active, old resources can be removed when appropriate.
This sounds simple, but update behavior should be tested carefully because a poorly managed cache can create confusing production bugs.

Security Considerations

Offline storage also introduces security questions.
Developers should ask:

  • What information is being stored locally?
  • Could another person access the device?
  • Does cached data contain sensitive information?
  • How long should local data remain available?
  • What happens after logout?
  • What happens if an account changes?
  • Can unauthorized requests be replayed?

A service worker should not be treated as a trusted storage mechanism for sensitive information simply because it runs in the browser.
Authentication and authorization still need to be enforced by the backend.
The client should also avoid assuming that locally stored data is inherently trustworthy.
For applications handling sensitive information, the offline architecture should be reviewed alongside the application's broader security model.

A Practical PWA Project for Developers

A good way to learn these concepts is to build a small offline-first application.
A personal expense tracker is a useful example.

Stage 1: Build the normal web application

Create:

  • Frontend interface
  • Backend API
  • Database
  • Authentication
  • Expense records

Stage 2: Add a web app manifest

Define the application's name, icons, start URL, and display behavior.

Stage 3: Add a service worker

Cache the application shell and selected static resources.

Stage 4: Add offline functionality

Allow users to view appropriate previously loaded information when disconnected.

Stage 5: Add local data storage

Store new expense entries locally when the network is unavailable.

Stage 6: Add synchronization

When connectivity returns, send appropriate local changes to the backend.

Stage 7: Handle conflicts and failures

Consider duplicate submissions, failed synchronization, outdated records, and interrupted requests.

Stage 8: Test real-world conditions

Do not test only on a fast development connection.
Try:

  • Offline mode
  • Slow connections
  • Interrupted requests
  • Browser restarts
  • Old cached versions
  • Multiple devices

This project can teach frontend development, APIs, databases, browser storage, service workers, deployment, and data consistency in one practical workflow.

When Should You Build a PWA?

PWAs are not automatically appropriate for every website.
A simple informational website may not need offline functionality or installation capabilities.
A PWA becomes more interesting when users repeatedly return to an application and benefit from features such as:

  • Offline access
  • Installability
  • Local data
  • Background work
  • Fast access to previously loaded content
  • Device-level integration supported by the platform

The decision should therefore start with the user's needs, not the technology.
Ask:

Would offline access or an app-like experience solve a real problem for the users?

If the answer is no, adding PWA infrastructure may simply create unnecessary maintenance.

The Bigger Full-Stack Lesson

Progressive Web Apps demonstrate an important shift in how developers can think about web applications.
A traditional application often assumes:
User → Browser → Network → Server

An offline-first application introduces more possibilities:
                   ┌── Cache
                    │
User → Browser → Service Worker
                    │
                    └── Network → Server → Database

Now the developer has to consider local state, network state, synchronization, caching, updates, and failure recovery.
That complexity is not always necessary. But when an application genuinely needs to remain useful under unreliable network conditions, these tools can make the web platform considerably more capable.
The most important lesson is not to memorize service worker APIs or PWA configuration files. It is to learn how to design an application around real-world conditions.
For developers building broader expertise in frontend development, backend systems, APIs, databases, and practical web applications, the Eduonix Lifetime Full Stack Web Development Membership can be explored as one structured learning resource alongside hands-on projects.
Build something small. Take away its network connection. Observe what breaks. Then redesign the application so that it handles those failures deliberately.
That exercise can teach more about real-world web engineering than simply building another application that works perfectly on localhost.

Top comments (0)