๐ Website That Works Completely Offline
๐๐จ๐ข๐ง ๐๐จ๐ฆ๐ฆ๐ฎ๐ง๐ข๐ญ๐ฒ - ๐๐๐๐๐ ๐๐๐๐Want to build a website that keeps working even when the internet disappears?
In this guide, we'll turn a normal HTML/CSS/JavaScript website into an offline-first Progressive Web App (PWA) using a Service Worker + Cache API + Web App Manifest.
The Service Worker can intercept requests and serve previously cached files when the network isn't available.
What we'll build
- Normal website UI
- Offline caching
- Offline fallback page
- Installable PWA
- Local task/data storage
- Online/offline indicator
- Automatic cache updates
- Works after the internet is disconnected
๐งฐ What You Need
You only need:
- HTML
- CSS
- JavaScript
- A browser
- A local web server
You don't need React, Node.js or a database for this basic version.
1. Create the Project
Create a folder:
offline-website/
Inside it create:
offline-website/ โ โโโ index.html โโโ style.css โโโ app.js โโโ sw.js โโโ manifest.json โโโ offline.html
2. Create the Main Website
Open:
index.html
Paste:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
>
<meta
name="theme-color"
content="#2563eb"
>
<title>Offline Workspace</title>
<link
rel="stylesheet"
href="style.css"
>
<link
rel="manifest"
href="manifest.json"
>
</head>
<body>
<header>
<h1>Offline Workspace</h1>
<div id="status">
Checking connection...
</div>
</header>
<main>
<section class="hero">
<h2>
Your Website,
Even Without Internet
</h2>
<p>
This page can continue working
when the network disappears.
</p>
<button id="saveButton">
Save Offline Note
</button>
</section>
<section class="card">
<h3>๐ Your Offline Note</h3>
<textarea
id="note"
placeholder="Write something..."
></textarea>
<button id="saveNote">
Save Note
</button>
<button id="clearNote">
Clear Note
</button>
</section>
<section class="card">
<h3>๐ Connection Status</h3>
<p id="connectionText">
Checking...
</p>
</section>
</main>
<script src="app.js"></script>
</body>
</html>
3. Create the Design
Open:
style.css
Paste:
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family:
Arial,
sans-serif;
background: #f3f4f6;
color: #111827;
}
header {
background: #2563eb;
color: white;
padding: 25px;
text-align: center;
}
header h1 {
margin: 0 0 8px;
}
#status {
font-size: 14px;
opacity: .9;
}
main {
max-width: 800px;
margin: auto;
padding: 25px;
}
.hero {
background: white;
padding: 30px;
border-radius: 18px;
margin-bottom: 20px;
box-shadow:
0 5px 20px
rgba(0,0,0,.06);
}
.hero h2 {
font-size: 32px;
margin-top: 0;
}
.card {
background: white;
padding: 25px;
border-radius: 18px;
margin-bottom: 20px;
box-shadow:
0 5px 20px
rgba(0,0,0,.06);
}
textarea {
width: 100%;
min-height: 180px;
padding: 15px;
border:
1px solid #d1d5db;
border-radius: 10px;
resize: vertical;
font-size: 16px;
margin-bottom: 12px;
}
button {
padding: 12px 18px;
border: 0;
border-radius: 9px;
background: #2563eb;
color: white;
cursor: pointer;
margin-right: 8px;
margin-bottom: 8px;
}
button:hover {
opacity: .9;
}
4. Add the JavaScript
Open:
app.js
Paste:
const statusElement =
document.getElementById("status");
const connectionText =
document.getElementById("connectionText");
const note =
document.getElementById("note");
const saveNote =
document.getElementById("saveNote");
const clearNote =
document.getElementById("clearNote");
function updateConnectionStatus() {
if (navigator.onLine) {
statusElement.textContent =
"๐ข Online";
connectionText.textContent =
"You are currently online.";
} else {
statusElement.textContent =
"๐ด Offline";
connectionText.textContent =
"You are offline. The cached website is still available.";
}
}
window.addEventListener(
"online",
updateConnectionStatus
);
window.addEventListener(
"offline",
updateConnectionStatus
);
saveNote.addEventListener(
"click",
() => {
localStorage.setItem(
"offlineNote",
note.value
);
alert(
"Note saved on this device."
);
}
);
clearNote.addEventListener(
"click",
() => {
localStorage.removeItem(
"offlineNote"
);
note.value = "";
}
);
const savedNote =
localStorage.getItem(
"offlineNote"
);
if (savedNote) {
note.value = savedNote;
}
updateConnectionStatus();
if ("serviceWorker" in navigator) {
window.addEventListener(
"load",
async () => {
try {
await navigator.serviceWorker.register(
"sw.js"
);
console.log(
"Service Worker registered."
);
} catch (error) {
console.error(
"Service Worker registration failed:",
error
);
}
}
);
}
The note is stored using localStorage, so it remains available on that browser/device even when the network is unavailable.
5. Create the Service Worker
This is the most important file.
Create:
sw.js
Paste:
const CACHE_NAME =
"offline-workspace-v1";
const FILES_TO_CACHE = [
"/",
"/index.html",
"/style.css",
"/app.js",
"/offline.html",
"/manifest.json"
];
self.addEventListener(
"install",
event => {
event.waitUntil(
caches.open(
CACHE_NAME
).then(
cache => {
return cache.addAll(
FILES_TO_CACHE
);
}
)
);
self.skipWaiting();
}
);
self.addEventListener(
"activate",
event => {
event.waitUntil(
caches.keys()
.then(
cacheNames => {
return Promise.all(
cacheNames
.filter(
name =>
name !==
CACHE_NAME
)
.map(
name =>
caches.delete(
name
)
)
);
}
)
);
self.clients.claim();
}
);
self.addEventListener(
"fetch",
event => {
if (
event.request.method !==
"GET"
) {
return;
}
event.respondWith(
fetch(event.request)
.then(
response => {
const copy =
response.clone();
caches.open(
CACHE_NAME
).then(
cache => {
cache.put(
event.request,
copy
);
}
);
return response;
}
)
.catch(
async () => {
const cached =
await caches.match(
event.request
);
if (cached) {
return cached;
}
if (
event.request.mode ===
"navigate"
) {
return caches.match(
"/offline.html"
);
}
return new Response(
"Offline",
{
status: 503,
headers: {
"Content-Type":
"text/plain"
}
}
);
}
)
);
}
);
This uses a network-first approach:
Request
โ
Try Internet
โ
Success?
โโYESโโโโโโโโโโโโโโ
โ โ
Return Save
response in cache
NO
โ
Check cache
โ
Found?
โ
Return cached file
Service workers can intercept fetches and return cached responses, which is the core mechanism behind offline PWAs.
6. Create the Offline Page
Create:
offline.html
Paste:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
>
<title>You're Offline</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-family: Arial;
background: #f3f4f6;
color: #111827;
}
.box {
background: white;
padding: 40px;
border-radius: 18px;
max-width: 500px;
box-shadow:
0 10px 30px
rgba(0,0,0,.08);
}
h1 {
font-size: 35px;
}
</style>
</head>
<body>
<div class="box">
<h1>๐ก You're Offline</h1>
<p>
The internet connection is unavailable,
but your offline website is still running.
</p>
</div>
</body>
</html>
7. Create the PWA Manifest
Create:
manifest.json
Paste:
{
"name": "Offline Workspace",
"short_name": "Offline App",
"start_url": "/",
"display": "standalone",
"background_color": "#f3f4f6",
"theme_color": "#2563eb",
"description": "A website that works offline.",
"icons": [
{
"src": "icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
The manifest tells the browser how the web app should appear and behave when installed.
8. Add the App Icons
Put these files in the project root:
icon-192.png icon-512.png
Your final structure becomes:
offline-website/ โ โโโ index.html โโโ style.css โโโ app.js โโโ sw.js โโโ manifest.json โโโ offline.html โโโ icon-192.png โโโ icon-512.png
You can create a simple square icon using any image editor.
9. Don't Open index.html Directly
This is important.
Don't simply do:
file:///.../index.html
and expect the Service Worker to work.
Service Workers require a secure context such as HTTPS; browsers also allow localhost for local development.
10. Start a Local Server
If Python is installed, open the terminal inside:
offline-website/
Run:
python -m http.server 8000
Linux/macOS:
python3 -m http.server 8000
Now open:
http://localhost:8000
11. Install the Service Worker
Open the website once while connected to the internet.
The browser downloads:
index.html style.css app.js sw.js offline.html manifest.json
The Service Worker then places the important resources into the browser cache.
Pre-caching static resources during the Service Worker's install event is a standard offline-PWA pattern.
12. Test Offline Mode
Now open browser Developer Tools.
In Chrome:
F12
Then:
Network โ Offline
Refresh the website.
You should still see:
Offline Workspace ๐ด Offline Your Website, Even Without Internet
That's the important moment:
The website is now running without a network connection.
13. Test the Offline Note
Type:
This note was created offline.
Click:
Save Note
Disconnect the internet.
Refresh.
The note should still appear.
That's because:
localStorage
โ
Browser storage
โ
No Internet Required
For larger structured offline data, IndexedDB is another browser storage option; MDN lists it as suitable for significant amounts of structured data and files.
14. Test the Offline Fallback
While offline, try opening a page that wasn't cached.
The Service Worker checks:
Cache โ Not found โ Navigation request? โ YES โ offline.html
So instead of getting a normal browser network-error page, your website displays your own offline screen.
15. Make the Website Installable
Once deployed properly, supported browsers can offer an Install option.
Your manifest contains:
{
"name": "Offline Workspace",
"short_name": "Offline App",
"start_url": "/",
"display": "standalone"
}
For Chromium-based browsers, installability requirements include appropriate manifest fields and 192px/512px icons; the site also needs HTTPS or local development through localhost/127.0.0.1.
After installation, it can appear more like an app:
๐ฑ Home Screen
โ
Offline App
โ
Your Website
16. Deploy It Online
For other people to use your offline website, deploy it to an HTTPS host.
Possible options include:
GitHub Pages Netlify Vercel Cloudflare Pages Your own HTTPS server
The important part is that the website is served over HTTPS so the Service Worker can operate.
17. Understand What "Completely Offline" Means
This is important.
A website can work offline only with the content and functionality you've prepared for offline use.
For example:
HTML โ Offline CSS โ Offline JavaScript โ Offline Cached images โ Offline Saved notes โ Offline Cached pages โ Offline
But:
Live weather API โ Without internet Online payment โ Without internet Fresh news โ Without internet Server database โ Without internet Real-time multiplayer โ Without internet
A service worker can cache resources, but it cannot magically make an internet-dependent backend available when the network is disconnected. MDN also notes that caching is most appropriate for resources that don't require freshness, while some requests such as POST requests aren't appropriate for caching.
18. Add Offline Data Sync Later
Here's where it gets powerful.
Suppose a user creates a note while offline:
Write Note
โ
Save Locally
โ
Internet OFF
โ
Keep Working
When the internet returns:
Internet ON
โ
Detect connection
โ
Upload pending data
โ
Server saves it
โ
Mark as synced
You can build this using local storage/IndexedDB plus appropriate synchronization logic. Service workers also provide APIs for background operations, although browsers control when service workers run.
19. Update Your Website Later
Suppose you release:
v1
Then change the website to:
v2
Change:
const CACHE_NAME =
"offline-workspace-v1";
to:
const CACHE_NAME =
"offline-workspace-v2";
When the new Service Worker activates, the old cache is deleted by:
caches.keys()
and:
caches.delete()
This gives you a simple cache-versioning system.
20. Better Production Structure
For a larger offline website:
offline-app/ โ โโโ index.html โโโ offline.html โ โโโ css/ โ โโโ style.css โ โโโ js/ โ โโโ app.js โ โโโ images/ โ โโโ logo.png โ โโโ banner.jpg โ โโโ icons/ โ โโโ icon-192.png โ โโโ icon-512.png โ โโโ sw.js โโโ manifest.json
Then add:
โ Offline routing โ IndexedDB โ Data synchronization โ Cache versioning โ Install prompt โ Offline downloads โ Background sync โ Update notification
๐ Final Architecture
Your finished website works like this:
YOUR WEBSITE
โ
โโโโโโโโโโดโโโโโโโโโ
โ โ
Web Browser Service Worker
โ
โโโโโโโโโโโดโโโโโโโโโโ
โ โ
Network Cache
โ โ
Online Offline
โ โ
โโโโโโโโโโโฌโโโโโโโโโโ
โ
Your Web App
The key technologies are:
HTML + CSS + JavaScript + Service Worker + Cache API + Web App Manifest + localStorage / IndexedDB
That's enough to turn a normal website into a genuinely useful offline-first web application. PWAs use these technologies to provide offline operation, installability and app-like behavior.
๐ Useful Links
MDN Progressive Web Apps:
https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps
Service Worker API:
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
Using Service Workers:
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
PWA Offline & Background Operation:
PWA Caching:
https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching
PWA Installability:
https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable
Web App Manifest:
https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest
Cache API: