Capabilities · API
Web Share API
Published
navigator.share() hands a URL, title, text, or files to the operating system’s share sheet, so a PWA can send content to any app the user has installed without building its own share menu. navigator.canShare() validates the same payload synchronously and, unlike share(), works without a user gesture.
On desktop the API is partial: Chrome 89 and Edge 81 implement it on Windows and ChromeOS only (Chromium bugs 40542648 and 40729163 track macOS and Linux), Firefox 71 keeps it behind the dom.webshare.enabled preference, and Android WebView has no implementation (crbug.com/40540400). Safari 12.1 on macOS and 12.2 on iOS, Chrome 61 and Firefox 79 on Android support it without caveats.
Syntax
Section titled “Syntax”navigator.share()navigator.share(data)
navigator.canShare()navigator.canShare(data)share() returns a Promise<undefined> that resolves once the data has been transmitted to the chosen target, or to the OS when the target cannot confirm receipt. canShare() returns a boolean and is the only one of the two that may be called outside a user-activation handler. Both are [SecureContext]: on http:// origins other than localhost, navigator.share is undefined.
Parameters
Section titled “Parameters”Both methods take one optional data argument, a ShareData dictionary. Every member is optional on its own, but the dictionary must carry at least one of title, text, url, or a non-empty files; otherwise validation fails.
| Member | Type | Required | Description |
|---|---|---|---|
title |
USVString |
No | Title of the shared content. Mail targets use it as the subject; many messaging targets discard it. |
text |
USVString |
No | Free-form body text, sent alongside or instead of url. |
url |
USVString |
No | Absolute or relative URL, resolved against the document’s base URL before sharing; "" shares the current page. Only http:, https:, and schemes the browser safelists are sharable. |
files |
sequence<File> |
No | Files to share. { files: [] } with no other member is treated as an empty dictionary; { text: "x", files: [] } is accepted and the empty list ignored. |
Members the browser does not recognise are dropped silently (WebIDL dictionary semantics), so a browser that only knows title, text, and url will share those three and ignore files. Pass each member to canShare() on its own when you need to know that every one is supported.
Exceptions
Section titled “Exceptions”share() rejects its promise with one of the following DOMException names. The order below is the order the specification’s algorithm checks them in.
| Exception | Condition |
|---|---|
InvalidStateError |
The document is not fully active, or an earlier share() promise is still pending ([[sharePromise]] is not null). Chromium skips the second check on Android. |
NotAllowedError |
The web-share Permissions Policy denies the document (default allowlist is 'self', so a cross-origin iframe needs allow="web-share"); or the call has no transient user activation; or a file type is blocked for security reasons. |
TypeError |
“Validate share data” returned false: no member present; url fails to parse; url uses a local scheme, file:, javascript:, ws:, wss:, or any scheme that is not sharable; files is present but the browser does not support file sharing or judges a file potentially hostile. |
AbortError |
No share targets are available, or the user dismissed the share sheet. |
DataError |
The chosen target failed to start, or transmitting the data to it failed. |
canShare() throws nothing. It returns false for exactly the conditions that make share() reject with TypeError, and true otherwise, including when share() would later reject with AbortError because the user cancels (see the canShare() algorithm (w3.org)).
Examples
Section titled “Examples”Each example feature-detects first and shows what runs when support is absent. All three must be triggered from a user-activation handler such as click.
Sharing the current page with a clipboard fallback
Section titled “Sharing the current page with a clipboard fallback”Detect navigator.share before wiring the button; when it is missing (Firefox desktop with the preference off, Android WebView, any http:// origin) copy the URL instead and tell the user what happened. The share call itself must stay inside the click handler so the transient activation is still valid when share() runs.
const button = document.querySelector('#share');
button.addEventListener('click', async () => { const payload = { title: document.title, url: location.href };
if (!navigator.share) { await navigator.clipboard.writeText(location.href); button.textContent = 'Link copied'; return; }
try { await navigator.share(payload); } catch (err) { if (err.name === 'AbortError') return; // user closed the sheet console.error(`${err.name}: ${err.message}`); }});Awaiting anything (an analytics call, a fetch) before share() is the usual way to lose the activation; do the share first and record the event after the promise resolves.
Sharing a generated text file only when canShare() accepts it
Section titled “Sharing a generated text file only when canShare() accepts it”File sharing is narrower than URL sharing: Chrome 76 on Android added it, desktop Chrome 89 shares files on Windows and ChromeOS only, Safari 14 added files, and Firefox has no file sharing in any version (BCD api.Navigator.share.data_files_parameter). Build the File, ask canShare() whether this browser will take it, and fall back to a download link when it will not.
async function shareReport(csvText) { const file = new File([csvText], 'report.csv', { type: 'text/csv' });
if (navigator.canShare?.({ files: [file] })) { try { await navigator.share({ files: [file], title: 'Report' }); } catch (err) { if (err.name !== 'AbortError') throw err; } return; }
const link = document.createElement('a'); link.href = URL.createObjectURL(file); link.download = file.name; link.click(); URL.revokeObjectURL(link.href);}canShare({ files }) returns false both when the browser cannot share files at all and when it rejects this particular file, so the fallback covers both without separate checks.
Checking each member before a multi-member share
Section titled “Checking each member before a multi-member share”Because unknown members are ignored rather than rejected, a browser that lacks files support will still resolve a share({ text, files }) call, having shared only the text. Validate member by member when a partial share would mislead the user.
function supportedMembers(data) { if (!navigator.canShare) return []; return Object.entries(data) .filter(([key, value]) => navigator.canShare({ [key]: value })) .map(([key]) => key);}
const data = { title: 'Trip photos', text: 'From the weekend', files: photoFiles };const ok = supportedMembers(data);
if (ok.includes('files')) { await navigator.share(data);} else { await navigator.share({ title: data.title, text: data.text, url: galleryUrl });}The url branch gives the recipient a link to the same photos when the device cannot carry the files themselves.
See also
Section titled “See also”- Manifest share_target, the receiving side of sharing
- Receive shared content (share target)
- Async Clipboard API, the usual fallback when
navigator.shareis undefined - Web Share API: share() method (w3.org)
- Web Share API: validate share data (w3.org)
- Chromium bug 40542648: Web Share on macOS (crbug.com)
- Chromium bug 40540400: Web Share in Android WebView (crbug.com)
Specifications
| Specification | Status |
|---|---|
| Web Share API | W3C |
| Web Share API: share() method | W3C |
| Web Share API: canShare() method | W3C |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop) | Partial | 89 → 128 | high | source | 1 |
| Chrome (Android) | Yes | 61 | high | source | — |
| Edge (Desktop) | Partial | 81 → 93 | high | source | 2 |
| Firefox (Desktop) | Flag | 71 | high | source | 3 |
| Firefox (Android) | Yes | 79 | high | source | — |
| Safari (macOS) | Yes | 12.1 | high | source | — |
| Safari (iOS) | Yes | 12.2 | high | source | 4 |
| Samsung Internet | Yes | 8.0 | high | source | 5 |
| WebView (Android) | No | — | high | source | 6 |
Try it
Run this capability in the OpenPWA demo app: /demo/#share