Modern Browser APIs
Modern browsers expose powerful APIs that used to require plugins or native apps. The key principle is progressive enhancement: your app works without the API, and the API makes it better.
Feature Detection
Before using any modern API, check if it exists. Never assume the user's browser supports it.
// Good: check before using
if ('clipboard' in navigator) {
// Use Clipboard API
}
// Good: check with optional chaining
if (navigator.share) {
// Use Share API
}
// Bad: just call it and hope
navigator.clipboard.writeText('crash if unsupported');
Progressive Enhancement Pattern
function setupCopyButton(button, text) {
if (navigator.clipboard) {
button.addEventListener('click', () => {
navigator.clipboard.writeText(text);
button.textContent = 'Copied';
});
} else {
// Fallback: show text in a selectable input
button.addEventListener('click', () => {
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
button.textContent = 'Copied';
});
}
}
Clipboard API
The Clipboard API provides async methods to read from and write to the clipboard. It replaces the old synchronous document.execCommand('copy').
// Write text to clipboard
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('Copied to clipboard');
} catch (err) {
console.error('Clipboard write failed:', err);
}
}
// Read text from clipboard (requires permission)
async function pasteFromClipboard() {
try {
const text = await navigator.clipboard.readText();
console.log('Pasted:', text);
return text;
} catch (err) {
console.error('Clipboard read failed:', err);
}
}
Geolocation API
The Geolocation API provides the user's geographic position. It always requires explicit user permission.
function getLocation() {
if (!navigator.geolocation) {
console.log('Geolocation not supported');
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(`Lat: ${position.coords.latitude}`);
console.log(`Lon: ${position.coords.longitude}`);
console.log(`Accuracy: ${position.coords.accuracy}m`);
},
(error) => {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log('User denied location access');
break;
case error.POSITION_UNAVAILABLE:
console.log('Location unavailable');
break;
case error.TIMEOUT:
console.log('Request timed out');
break;
}
},
{ enableHighAccuracy: true, timeout: 10000 }
);
}
Watching Position
const watchId = navigator.geolocation.watchPosition((position) => {
updateMap(position.coords.latitude, position.coords.longitude);
});
// Stop watching
navigator.geolocation.clearWatch(watchId);
Notifications API
The Notifications API shows system-level notifications to the user, even when the browser tab is not focused.
async function showNotification(title, body) {
if (!('Notification' in window)) {
console.log('Notifications not supported');
return;
}
if (Notification.permission === 'default') {
await Notification.requestPermission();
}
if (Notification.permission === 'granted') {
new Notification(title, {
body,
icon: '/icon-192.png',
badge: '/badge-72.png',
});
}
}
showNotification('Task complete', 'Your export is ready to download');
Fullscreen API
The Fullscreen API lets you display an element in fullscreen mode. Useful for video players, presentations, and games.
const videoContainer = document.getElementById('video-container');
async function toggleFullscreen() {
if (!document.fullscreenElement) {
await videoContainer.requestFullscreen();
} else {
await document.exitFullscreen();
}
}
// Listen for fullscreen changes
document.addEventListener('fullscreenchange', () => {
const isFullscreen = !!document.fullscreenElement;
console.log('Fullscreen:', isFullscreen);
});
Share API
The Share API invokes the native sharing dialog on mobile devices. It falls back gracefully because it is only available on supporting platforms (mainly mobile).
async function shareContent() {
if (!navigator.share) {
// Fallback: show a copy-link button
copyToClipboard(window.location.href);
return;
}
try {
await navigator.share({
title: 'Great Article',
text: 'Check out this article about browser APIs',
url: window.location.href,
});
} catch (err) {
if (err.name !== 'AbortError') {
console.error('Share failed:', err);
}
}
}
Speech API
The Web Speech API provides speech recognition (speech-to-text) and speech synthesis (text-to-speech).
// Text-to-speech
function speak(text) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.rate = 1.0;
utterance.lang = 'en-US';
speechSynthesis.speak(utterance);
}
// Speech-to-text
if ('webkitSpeechRecognition' in window) {
const recognition = new webkitSpeechRecognition();
recognition.lang = 'en-US';
recognition.onresult = (event) => {
console.log('You said:', event.results[0][0].transcript);
};
recognition.start();
}
The Permissions API
The Permissions API lets you check the status of various permissions without triggering a prompt.
async function checkPermissions() {
const permissions = ['geolocation', 'notifications', 'clipboard-read'];
for (const name of permissions) {
try {
const result = await navigator.permissions.query({ name });
console.log(`${name}: ${result.state}`); // 'granted', 'denied', or 'prompt'
result.addEventListener('change', () => {
console.log(`${name} changed to: ${result.state}`);
});
} catch {
console.log(`${name}: not supported by Permissions API`);
}
}
}
Permission states are granted, denied, or prompt. Adapt your UI based on the current state instead of blindly requesting permission.
Progressive Enhancement
The principle behind all modern browser APIs: the application works without the API, but the API makes it better.
// The core feature always works
function shareArticle(url) {
// Best: native share dialog
if (navigator.share) {
return navigator.share({ url });
}
// Good: clipboard copy
if (navigator.clipboard) {
navigator.clipboard.writeText(url);
showToast('Link copied');
return;
}
// Fallback: show the URL for manual copy
prompt('Copy this link:', url);
}
Building a Progressive Feature
- Build the core feature without the API
- Detect API support
- Enhance the experience when the API is available
- Handle permission denials gracefully
- Provide feedback for both paths
Common Pitfalls
- Requesting permissions on page load: users deny permissions they do not understand. Request permission in context (e.g., when they click "Share my location").
- No fallback for unsupported APIs: the Share API is not available on desktop Firefox. Always have a fallback path.
- Ignoring permission denial: if a user denies geolocation, do not keep asking. Show a manual input instead.
- Not handling async errors: most modern APIs are async and can throw. Always use try/catch.
- Assuming HTTPS: most modern APIs (Clipboard, Geolocation, Notifications, Service Workers) require HTTPS. They silently fail or are unavailable on HTTP.
- Speech synthesis queue overflow: calling
speechSynthesis.speak()repeatedly queues utterances. Cancel previous ones if needed.
Key Takeaways
- Always feature-detect before using any modern API
- Progressive enhancement means the app works without the API and is better with it
- The Clipboard API replaces
document.execCommand('copy')with an async, Promise-based approach - Geolocation, Notifications, and Clipboard require user permission and HTTPS
- The Share API provides native mobile sharing dialogs with a simple JavaScript call
- The Permissions API lets you check permission state without triggering a prompt
- Request permissions in context, not on page load, to avoid immediate denials