Content Index API: A Brief Overview

online and offline printed on cubes
The Content Index API lets PWAs register offline-ready pages with the browser, making saved articles and other content easier to discover and manage.

The Content Index API is an experimental Web API that lets developers register offline-enabled web pages with the browser so users can more easily discover and reopen them later. It’s aimed at Progressive Web Apps that cache content for offline use—such as news sites saving articles in the background or apps making downloaded content available when the network is unavailable.

What it does

How it fits into the platform

The API is an extension to service workers.

The entry point is ServiceWorkerRegistration.index (a ContentIndex instance).

Core methods:

Important constraints and best practices

The contentdelete event

A contentdelete event can be delivered to the service worker when the user agent removes indexed content via its own built-in UI. MDN notes this event does not fire when you remove items yourself using ContentIndex.delete(). This event is mainly useful for cleanup—e.g., deleting associated cached resources when the system removes an indexed entry.

 
				
					<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Content Index API Demo</title>
    <link data-wphbdelayedstyle="style.css" rel="stylesheet" />
    <script type="wphb-delay-type" src="contentIndex_API.js" defer></script>
</head>
    
<body>
    <h1>Content Index API Demo</h1>
    <p>Status: <span id="status">Checking support...</span></p>
    
    <!-- Demo Controls -->
    <div id="controls" style="display:none;">
        <button id="btn-add">1. Index Content (Add)</button>
        <button id="btn-check">2. Check All Entries</button>
        <button id="btn-remove">3. Un-Index Content (Remove)</button>
    </div>
    
    <!-- Logs for the demo video -->
    <pre id="logs"></pre>

<script type="text/javascript" id="wphb-delayed-styles-js">
			(function () {
				const events = ["keydown", "mousemove", "wheel", "touchmove", "touchstart", "touchend"];
				function wphb_load_delayed_stylesheets() {
					document.querySelectorAll("link[data-wphbdelayedstyle]").forEach(function (element) {
						element.setAttribute("href", element.getAttribute("data-wphbdelayedstyle"));
					}),
						 events.forEach(function (event) {
						  window.removeEventListener(event, wphb_load_delayed_stylesheets, { passive: true });
						});
				}
			   events.forEach(function (event) {
				window.addEventListener(event, wphb_load_delayed_stylesheets, { passive: true });
			  });
			})();
		</script></body>

</html>
				
			
				
					body {
    font-family: sans-serif;
    margin: 2em;
    background-color: #f4f4f4;
    color: #333;
}

#logs {
    background: #f4f4f4;
    padding: 10px;
    margin-top: 20px;
}
				
			
				
					// Standard cache setup
self.addEventListener('install', (event) => {
	// In a real app, you MUST cache the URL you intend to index.
	// The browser may refuse to index content if it detects it isn't cached.
	const urlsToCache = [
		'./',
		'./index.html',
		'./contentIndex_API.js',
		'./article.html',
		'./icon.svg'
	];

	event.waitUntil(
		caches.open('demo-cache-v1').then((cache) => {
			return cache.addAll(urlsToCache);
		})
	);
	self.skipWaiting();
});

// 6. HANDLING SYSTEM DELETION
// This event fires if the USER deletes the content from the
// Browser/System UI (not from your web app's UI).
self.addEventListener('contentdelete', (event) => {
	console.log(`Content deleted by user: ${event.id}`);

	// Use this opportunity to clean up related cache data
	event.waitUntil(
		caches.open('demo-cache-v1').then((cache) => {
			// Example: If the user deletes the index entry,
			// we might want to remove the actual file from cache.
			// return cache.delete('/article.html');
		})
	);
});

				
			
				
					// HELPER: Simple logger for the demo
const log = (msg) => {
	const logEl = document.getElementById('logs');
	logEl.textContent += `> ${msg}\n`;
	console.log(msg);
};

// 1. REGISTER SERVICE WORKER
const init = async () => {
	if (!('serviceWorker' in navigator)) {
		document.getElementById('status').textContent =
			'Service Worker not supported.';
		return;
	}

	try {
		const registration = await navigator.serviceWorker.register('sw.js');
		await navigator.serviceWorker.ready;

		// 2. FEATURE DETECTION
		if (!('index' in registration)) {
			document.getElementById('status').textContent =
				'Content Index API NOT supported in this browser.';
			return;
		}

		document.getElementById('status').textContent = 'Ready. API Supported.';
		document.getElementById('controls').style.display = 'block';

		document.getElementById('btn-add').onclick = () => addToIndex(registration);
		document.getElementById('btn-check').onclick = () =>
			checkIndex(registration);
		document.getElementById('btn-remove').onclick = () =>
			removeFromIndex(registration);
	} catch (error) {
		console.error('SW Registration failed', error);
	}
};

// 3. ADDING CONTENT TO THE INDEX
const addToIndex = async (registration) => {
	try {
		await registration.index.add({
			// Unique ID for this item
			id: 'article-123',

			// The URL to launch. Must match the cached file.
			url: './article.html',

			// Display metadata
			title: 'Offline Article Demo',
			description:
				'A sample article demonstrating offline content indexing capabilities.',
			category: 'article',

			icons: [
				{
					src: './icon.svg', // Points to local file
					sizes: '128x128',
					type: 'image/svg+xml' // Correct MIME type for SVG
				}
			]
		});
		log('Success: "article-123" added to Content Index.');
	} catch (e) {
		log(`Error adding to index: ${e.message}`);
	}
};

// 4. RETRIEVING INDEXED CONTENT
const checkIndex = async (registration) => {
	const entries = await registration.index.getAll();
	log(`Current Index Count: ${entries.length}`);

	for (const entry of entries) {
		log(` - Found: ${entry.title} (ID: ${entry.id})`);
	}
};

// 5. REMOVING CONTENT
const removeFromIndex = async (registration) => {
	await registration.index.delete('article-123');
	log('Success: "article-123" removed from Content Index.');
};

init();

				
			
Share the Post:

Related Posts

small_c_popup.png

Need help?

Let's have a chat...


Login

Jump Back In!

Here at Webolution Designs, we love to learn. This includes sharing things we have learned with you. 

Register

Begin Your Learning Journey Today!

Come back inside to continue your learning journey.