Articles

The Web Share API Explained: Native OS Share Sheets

The Web Share API lets a web page open the device's native share sheet to send text, links, or files to other apps, no clipboard hack required.

Takina Takina · · 4 min read
Abstract icon representing a browser web API

The Web Share API is a browser API that lets a web page invoke the operating system’s native share sheet — the same panel a native app uses to send a link to Messages, Mail, or a social app — directly from JavaScript. Instead of a page building its own row of platform-specific share buttons, it calls navigator.share() and lets the OS present whatever share targets are actually installed.

What it replaces

Before this API, “share” buttons on the web meant one of two things: a row of hardcoded icons linking to https://twitter.com/intent/tweet?...-style URLs for a handful of major platforms, or a “copy link” button paired with a toast telling the user to paste it themselves. Both approaches have the same weakness — they’re frozen at build time. A hardcoded row of share icons can’t offer AirDrop, can’t offer WhatsApp for users who have it installed, and needs a code change every time a new platform becomes popular enough to add.

navigator.share() sidesteps this entirely by not enumerating destinations at all. The page just says “I have this content to share,” and the OS shows every app on the device registered as a share target — which on a phone is often a dozen or more, none of which the page’s developer had to know about in advance.

Basic usage

async function shareArticle() {
  if (!navigator.share) {
    // Fall back to a copy-to-clipboard button, or your own share menu.
    return;
  }

  try {
    await navigator.share({
      title: "How virtual scrolling works",
      text: "A good explainer on windowing long lists.",
      url: "https://example.com/blog/virtual-scrolling-explained/",
    });
  } catch (err) {
    // AbortError fires when the user dismisses the share sheet — not a real error.
    if (err.name !== "AbortError") {
      console.error("Share failed:", err);
    }
  }
}

The share() method returns a promise that resolves once the user picks a target and the OS hands off the content — not once the recipient app has “finished” doing anything with it, which is outside the browser’s visibility. If the user cancels the share sheet, the promise rejects with an AbortError, which is worth handling separately from a genuine failure.

Feature detection is mandatory

Support for navigator.share() is inconsistent enough — some desktop browsers don’t implement it at all, and behavior differs by OS — that every call needs to be guarded by checking if (navigator.share) first, exactly the way you’d feature-detect any other browser API before using it. There’s no polyfill that can conjure a native share sheet on a browser that doesn’t support one; the fallback has to be a real alternative UI, typically a manual “copy link” affordance backed by the Clipboard API.

Sharing files: navigator.canShare()

A newer extension to the API, the Web Share API Level 2, allows sharing files — images, PDFs, generated exports — not just text and URLs:

async function shareImage(file) {
  if (navigator.canShare && navigator.canShare({ files: [file] })) {
    await navigator.share({
      files: [file],
      title: "Generated chart",
    });
  }
}

canShare() exists as a separate synchronous check because file sharing support is even less consistent than text/URL sharing, and because certain MIME types may be rejected by the OS even on a browser that otherwise supports the API. Checking before calling share() avoids throwing on inputs the platform can’t handle — always verify with canShare() when files are involved, since a plain navigator.share check only confirms the API exists, not that this particular payload is shareable.

Where it fits alongside other APIs

The Web Share API is one of a family of browser APIs that expose native OS capabilities to installed PWAs and ordinary web pages alike, alongside things like web push notifications and background sync through a service worker. None of these require an app store; they’re standard web APIs gated behind a secure context (HTTPS) and, for share and push in particular, a user gesture — you can’t call navigator.share() from a page-load handler, only in response to a click or tap.

There’s also a receiving counterpart, the Web Share Target API, which lets an installed PWA register itself as a destination other apps can share to — the reverse direction of the flow described above, configured through the PWA’s web app manifest rather than called from JavaScript.

Common pitfalls

  • Calling it outside a user gesture. Like requestFullscreen() or clipboard writes, share() typically only works when invoked synchronously from a click handler; calling it from a setTimeout or an async callback after the click often silently fails.
  • Not handling AbortError. Treating a user simply closing the share sheet as an application error produces confusing console noise and, worse, confusing UI feedback (“Something went wrong!” when nothing did).
  • Assuming url is always separate from text. Some target apps concatenate text and url into a single message; others treat them as distinct fields. Don’t rely on exact formatting on the receiving end.
  • Skipping the fallback. Because desktop support lags mobile support, a share button that only works via navigator.share() effectively disappears for a meaningful share of desktop visitors unless a manual fallback is wired up.

The takeaway

The Web Share API replaces a hardcoded row of platform icons with a single call to the operating system’s own share sheet, which stays current with whatever apps a user actually has installed. Feature-detect before calling it, treat a user-cancelled share as a no-op rather than an error, and keep a manual clipboard-based fallback for browsers — mostly on desktop — where native support isn’t there yet.

Takina Takina · · 4 min read

Object.groupBy() in JavaScript, Explained

Object.groupBy() buckets array items by a key you compute, replacing the reduce() boilerplate developers have written for years. How it works.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

Svelte 5 Runes Explained

Svelte 5 runes like $state and $derived replace the old reactive-assignment magic with explicit function calls that work anywhere in a file.

#JavaScript #Frameworks #Web Development