3 min read
On this page

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

  1. Build the core feature without the API
  2. Detect API support
  3. Enhance the experience when the API is available
  4. Handle permission denials gracefully
  5. 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