DEV Community

Arvind Jolly
Arvind Jolly

Posted on

Building a High-Performance Progressive Web App Without React or Next.js

Lessons learned from improving performance, caching, and user experience in a production web application.

The Problem

When we set out to build SAGE (School of Ancient Geomantic Education), our focus was not initially on Progressive Web Apps.

SAGE combines traditional systems such as Western Geomancy and Indian Ramal Shastra with modern web technologies, educational resources, interactive calculators, and AI-assisted interpretation.

As the platform evolved, performance, reliability, and user experience became increasingly important concerns.

Our users access the platform from a wide variety of devices and network conditions. Some use desktop computers with fast broadband connections. Others rely on mobile networks where latency and bandwidth can vary significantly.

We wanted the experience to feel fast, responsive, and dependable regardless of connection quality.

That led us to explore:

  • Service Workers
  • Intelligent caching strategies
  • Firebase Hosting optimizations
  • Progressive Web App technologies
  • Performance-focused UX design

The goal was not to create a fully offline oracle. AI-assisted readings continue to require backend services and network connectivity.

Instead, the objective was to improve performance, reduce unnecessary network requests, create a more resilient user experience, and make the platform feel more like a modern application than a traditional website.

This is the story of how we transformed a straightforward HTML + Firebase Hosting application into a faster, more polished Progressive Web App—without React, Next.js, or heavyweight build pipelines.

The Core Principle: Treat the Service Worker as a Router

Many PWA tutorials present caching strategies as isolated techniques:

  • Cache-first
  • Network-first
  • Stale-while-revalidate

In practice, a production application usually needs several of them simultaneously.

The most useful mental model we found was to think of the service worker as a routing layer.

Different content types receive different treatment.

Content Strategy
Images, CSS, fonts, static JavaScript Cache-first
HTML documents Network-first
API requests Pass-through
Configuration modules Always fresh

The important question is not:

Which caching strategy should I use?

The important question is:

Which caching strategy should this particular resource use?

Once we adopted that mindset, the service worker became dramatically easier to reason about.

Public Content and Private Content Are Different Problems

One mistake I see frequently in PWA discussions is treating all pages equally.

They aren't.

Some pages are:

  • Public
  • Anonymous
  • Safe to cache

Others are:

  • User-specific
  • Session-aware
  • Privacy-sensitive

The boundary matters.

For example, serving a stale public calculator page is usually harmless.

Serving cached user-specific content to the wrong session is not.

Our solution was simple:

  • Public pages can be pre-cached.
  • Session-dependent pages are excluded from pre-caching.
  • Sensitive content is always fetched fresh.

The result is a much safer offline experience.

Never Cache the Service Worker

If there is one rule worth remembering, it is this:

Do not aggressively cache your service worker.

The service worker controls your entire update mechanism.

If the browser becomes stuck with an old service worker, every future deployment becomes harder.

Static assets can be immutable.

The service worker cannot.

Treat it as the control plane for your application.

Build an Escape Hatch

Eventually, every production application encounters one of these:

  • A bad deployment
  • Corrupted cache state
  • An update bug
  • A broken service worker

When that happens, users should not need to clear browser data manually.

We implemented a simple versioning mechanism that can:

  1. Detect an application version change
  2. Unregister outdated service workers
  3. Trigger a clean reload

Think of it as an emergency recovery procedure.

You may never need it.

When you do need it, you'll be glad it exists.

Why We Chose localStorage Instead of IndexedDB

This decision often surprises developers.

IndexedDB is usually presented as the "correct" storage solution for PWAs.

For large datasets, that's true.

For our use case, it wasn't.

The application only needed to store:

  • Small pieces of user state
  • Temporary workflow data
  • Lightweight JSON payloads

Each payload was only a few kilobytes.

The benefits of localStorage were compelling:

  • Simplicity
  • Synchronous access
  • No schema management
  • Minimal implementation complexity

Could IndexedDB have worked?

Absolutely.

Would it have improved the user experience?

Not meaningfully.

Sometimes the simplest solution is the right solution.

The Most Overlooked Performance Problem: Fonts

Many performance discussions focus on JavaScript bundles.

In our case, fonts were the bigger challenge.

The application supports multiple writing systems, including:

  • Latin
  • Devanagari
  • Arabic
  • Japanese
  • Chinese

Without careful loading strategies, typography can easily become the largest source of perceived latency.

Three techniques made the difference:

  1. Using display=swap
  2. Adding preconnect hints
  3. Caching font resources after the first visit

After the initial load, typography effectively became free.

Offline UX Is More Important Than Offline Technology

Most developers focus on the technical side of offline support.

Users don't care about your caching strategy.

They care about what happens when connectivity disappears.

When a page isn't available offline, users should never encounter a browser error screen.

Instead, provide:

  • A branded fallback page
  • Clear messaging
  • A recovery path
  • Consistent visual identity

The goal is not merely functionality.

The goal is preserving trust.

Fast Applications Need Feedback

An unexpected challenge emerged once everything was cached.

The application became extremely fast.

Page transitions often completed in under 100 milliseconds.

Users interpreted this as abrupt rather than responsive.

The solution wasn't optimization.

The solution was intentional motion.

Subtle micro-interactions:

  • Entry animations
  • State indicators
  • Ambient visual feedback

made the interface feel more polished despite adding virtually no latency.

This was a reminder that perceived performance and measured performance are not always the same thing.

What We Learned

After deploying and maintaining the application, a few principles stood out:

1. Service workers are routing infrastructure

Treat them like routers rather than cache containers.

2. Not everything belongs in IndexedDB

Simple state often benefits more from simplicity than scalability.

3. Offline experiences are UX problems first

Caching is only the implementation detail.

4. Every PWA needs a recovery mechanism

Eventually something will go wrong.

Plan for it.

5. Fast interfaces still need visual feedback

Perceived quality matters as much as measured speed.

6. Simplicity scales surprisingly far

For many applications, a lightweight architecture can outperform a far more complex framework-based stack.

Final Thoughts

The modern web platform already provides most of the tools needed to build capable offline applications.

Service Workers, Cache Storage, localStorage, and modern browser APIs are remarkably powerful when combined thoughtfully.

The biggest lesson wasn't technical.

It was architectural.

Offline support works best when it is treated as a product requirement from the beginning rather than an enhancement added later.

When that happens, the result feels less like a website and more like an application that simply happens to run on the web.

A Note on Offline Functionality

The techniques discussed in this article focus primarily on performance optimization, caching, installability, and user experience improvements.

While certain assets and resources benefit from browser caching, AI-assisted readings and other server-dependent functionality continue to require network connectivity and backend processing.

The goal was to build a faster and more resilient web experience rather than a fully offline application.


What has been your biggest challenge building PWAs in production? I'd be interested to hear what strategies have worked (or failed) for you.

About the Project

SAGE (School of Ancient Geomantic Education) is a modern geomancy platform that combines traditional Western Geomancy and Indian Ramal Shastra with contemporary software engineering.

The platform provides:

  • Free geomantic calculators and educational tools
  • Daily oracle readings
  • Premium AI-assisted geomantic consultations
  • Support for multiple languages
  • Offline-capable Progressive Web App functionality

Explore the project at dotsofdestiny.com and learn more about how ancient symbolic systems can be implemented using modern web technologies.

Top comments (1)