Content Index API: A Brief Overview

online and offline printed on cubes

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 href="style.css" rel="stylesheet" />
    <script 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>

</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();

				
			
more posts:
Online video streaming platform and digital content management concept with media player interface, cloud library, and multimedia files on laptop, representing content distribution, video marketing
Code

Breaking into the Pipeline: Real-Time Frame Manipulation with the Insertable Streams for MediaStreamTrack API

The experimental Insertable Streams for MediaStreamTrack API closes a long-standing gap in web multimedia by treating live camera feeds as standard Web Streams. By piping individual VideoFrame objects through a TransformStream and recording the transformed output via MediaRecorder, developers can execute frame-by-frame processing—like real-time filtering, watermarking, and computer vision overlays—without relying on hidden canvas hacks.

Discover More »
Apps & Custom Software

What Building Our Own App Taught Us About Building Yours

When Hess WebTech built and launched Tericipe—an Android app that turns printed recipes into a searchable digital collection—in under three months, we had to live with every decision we usually advise clients on. From testing against real-world messy data and handling AI uncertainty to server-side payment security and post-launch maintenance, here are five crucial lessons from our own build that apply to almost any software project.

Discover More »