Skip to main content

HTML in Progressive Web Apps (PWAs)

Mentor's Note: A Progressive Web App is a website that behaves like a native app. It can be installed on your phone's home screen, work offline, send push notifications, and even access device hardware — all built with standard HTML, CSS, and JavaScript. The key word is progressive — it starts as a regular website and progressively gains superpowers.

What is a PWA?

A Progressive Web App is a web application that uses modern browser capabilities to deliver an app-like experience. PWAs are built with standard web technologies (HTML, CSS, JavaScript) but gain native-app features like offline support, home screen installation, and push notifications.

Core Requirements

A web app must meet three criteria to be considered a PWA:

  1. HTTPS — secure connection is mandatory
  2. Web App Manifest — a JSON file that defines how the app appears when installed
  3. Service Worker — a JavaScript file that runs in the background, handling caching and offline support

App Manifest (manifest.json)

The Web App Manifest is a JSON file that tells the browser how your app should behave when installed. You link it in HTML with <link rel="manifest">.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My PWA App</title>
<link rel="manifest" href="/manifest.json">
<!-- iOS fallback for older Safari versions -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
</head>

manifest.json

{
"name": "My PWA App",
"short_name": "PWA App",
"description": "A progressive web application built with HTML",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1a1a2e",
"orientation": "portrait-primary",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"categories": ["education", "utilities"],
"lang": "en-US",
"dir": "ltr"
}

Manifest Properties Summary

PropertyPurposeExample
nameFull app name shown on splash screen"My PWA App"
short_nameShort name for home screen (≤12 chars)"PWA App"
descriptionDescription for app stores / install prompts"A PWA for learning"
start_urlEntry point URL when launched"/" or "/?source=pwa"
displayHow the app is displayed"standalone", "fullscreen"
background_colorSplash screen background"#ffffff"
theme_colorBrowser chrome/toolbar colour"#1a1a2e"
orientationLocked orientation"portrait-primary"
iconsArray of app icons at various sizesSee above
scopeURLs the app controls"/"
categoriesApp store categories["education"]

Display Modes

The display property controls how the PWA looks when launched from the home screen:

ModeDescriptionBest For
fullscreenHides all browser UI — immersive full screenGames, video players, presentations
standaloneOpens in a separate window without address barMost apps — the standard PWA mode
minimal-uiShows minimal browser navigation (back, refresh)Utility and reference apps
browserOpens in a regular browser tab (default)Fallback — no app-like experience

Service Worker Registration in HTML

The service worker is registered via JavaScript in your HTML page. It acts as a network proxy — intercepting requests and serving cached responses when offline.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Offline-Ready PWA</title>
<link rel="manifest" href="/manifest.json">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>My Offline PWA</h1>
<p>This app works offline after the first visit.</p>

<script>
// Register the service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered:', registration.scope);
})
.catch(error => {
console.error('SW registration failed:', error);
});
});
}
</script>
</body>
</html>

A Minimal Service Worker (sw.js)

const CACHE_NAME = 'my-pwa-cache-v1';
const ASSETS_TO_CACHE = [
'/',
'/styles.css',
'/app.js',
'/icons/icon-192.png',
'/icons/icon-512.png'
];

// Install — cache the app shell
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(ASSETS_TO_CACHE))
.then(() => self.skipWaiting())
);
});

// Activate — clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(name => name !== CACHE_NAME)
.map(name => caches.delete(name))
);
})
);
});

// Fetch — serve from cache, fall back to network
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
.catch(() => {
// Offline fallback page
return caches.match('/offline.html');
})
);
});

The service worker runs in its own global scope (ServiceWorkerGlobalScope), not the page's window. It has no DOM access and can't directly interact with the page.


WebView — HTML Inside Native Apps

A WebView is a browser engine embedded inside a native mobile app. When you see HTML content inside an Android or iOS app (like a login screen, terms of service, or embedded article), it's likely running in a WebView.

WebView vs PWA

AspectWebView (in native app)PWA (installed from browser)
DistributionApp Store / Google PlayBrowser (no store required)
UpdatesRequires app store updateInstant — server-side update
CapabilitiesFull native API access (bridged)Limited to web APIs
InstallationUser downloads from app storeUser adds to home screen
SizeNative + WebView overheadSmall (HTML + cache)
OfflineDepends on implementationBuilt-in via service worker

Splash Screens and Icons

When a PWA launches, the browser shows a splash screen based on the manifest properties while the app loads.

How Splash Screens Work

The browser generates the splash screen automatically from:

  • background_color — fills the background
  • icons — the largest icon in the manifest is centred on the screen (ideally 512×512)
{
"name": "My PWA App",
"background_color": "#1a1a2e",
"icons": [
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}

Icon Requirements

SizePurpose
192×192Home screen icon, install prompt
512×512Splash screen, high-res displays
48×48Notification icon
96×96Favicon alternative

The "purpose": "maskable" flag tells the browser the icon has safe padding so it can be cropped into different shapes (circle, squircle, rounded square) depending on the platform.


Offline Support Concept

PWAs use the Cache API (available in the service worker) to store responses and serve them when the network is unavailable.

Caching Strategies

StrategyBest ForBehaviour
Cache FirstStatic assets (CSS, JS, icons)Serve from cache, update from network
Network FirstDynamic content (API data, articles)Try network first, fall back to cache
Stale-While-RevalidateMixed contentServe cached instantly, fetch update in background
Network OnlyReal-time data (chat, stocks)Always fetch fresh from network
Cache OnlyOffline-first appsNever hits the network

Try It Yourself — PWA Checklist

Build Your First PWA

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First PWA</title>
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#1a1a2e">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
<style>
body {
font-family: system-ui, sans-serif;
max-width: 600px;
margin: 2rem auto;
padding: 0 1rem;
text-align: center;
}
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 2rem;
margin: 2rem 0;
background: #f9f9f9;
}
button {
padding: 12px 24px;
font-size: 1rem;
background: #1a1a2e;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.status {
margin-top: 1rem;
padding: 0.5rem;
border-radius: 4px;
font-size: 0.9rem;
}
.online { background: #d4edda; color: #155724; }
.offline { background: #f8d7da; color: #721c24; }
</style>
</head>
<body>
<h1>My First PWA</h1>
<div class="card">
<p>This page should work offline after your first visit.</p>
<p id="connection-status" class="status online">✅ You are online</p>
<button onclick="fetchData()">Test Network</button>
<pre id="output"></pre>
</div>

<script>
// Register service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}

// Track online/offline status
window.addEventListener('online', () => {
document.getElementById('connection-status').textContent = '✅ You are online';
document.getElementById('connection-status').className = 'status online';
});
window.addEventListener('offline', () => {
document.getElementById('connection-status').textContent = '❌ You are offline';
document.getElementById('connection-status').className = 'status offline';
});

function fetchData() {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(r => r.json())
.then(data => {
document.getElementById('output').textContent = JSON.stringify(data, null, 2);
})
.catch(err => {
document.getElementById('output').textContent = 'Network error: ' + err.message;
});
}
</script>
</body>
</html>

PWA Readiness Checklist

  • HTTPS — site served over HTTPS (required for service workers)
  • manifest.json — valid manifest with name, icons, display, theme_color
  • Service worker — registered and handles fetch events
  • Offline support — app shell is cached and served when offline
  • Responsive design — works on mobile, tablet, desktop
  • Icons — 192×192 and 512×512 icons with maskable purpose
  • Theme color — matches your app's brand
  • Fast load — performs well on slow networks
  • Install prompt — browser shows add-to-home-screen dialog
  • Tested on device — opened on actual Android/iOS hardware

Pro tip: Use Lighthouse in Chrome DevTools (Audits tab) to run a PWA audit. It checks all the requirements and gives you a score with actionable feedback.


PWA vs Native Apps

FeaturePWANative App
InstallationVia browser (zero friction)App store download
UpdatesInstant (server push)App store review process
StorageBrowser storage (~50MB+)Full device storage
Push notificationsSupportedSupported
OfflineVia service worker cachingFull offline support
Device APIsLimited to web APIsFull native API access
SEOIndexable by search enginesNot indexable
CostSingle codebase (web)iOS + Android codebases