Receive shared content (share target)
Published Updated
In one line: declaring share_target in your Web App Manifest registers your
installed PWA in the OS native share sheet, so other apps can share URLs, text, titles,
and files directly into your app instead of you building a separate import flow.
1. Declare share_target in the manifest
Section titled “1. Declare share_target in the manifest”Per MDN, share_target requires an action (the URL that receives the share) and
params (the mapping of share fields to request parameters); method and enctype
are optional and default to "GET" and "application/x-www-form-urlencoded":
{ "share_target": { "action": "/share-handler", "method": "POST", "enctype": "multipart/form-data", "params": { "title": "title", "text": "text", "url": "url", "files": [ { "name": "media", "accept": ["image/*", "video/*"] } ] } }}The default GET with no files is enough for text/URL-only sharing; receiving files
requires explicitly setting "method": "POST" and "enctype": "multipart/form-data",
per MDN.
2. Handle the request in your service worker
Section titled “2. Handle the request in your service worker”Once the OS has offered your app in the share sheet and the user picks it, the browser
sends a POST (or GET) to action exactly like a form submission. Intercept it
in your service worker’s fetch handler and read the shared data. MDN says a POST share
request is ideally answered with a 303 redirect to avoid repeated POST submissions on
refresh, and Chrome for Developers demonstrates that pattern:
self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); if (event.request.method === 'POST' && url.pathname === '/share-handler') { event.respondWith((async () => { const formData = await event.request.formData(); const files = formData.getAll('media'); const cache = await caches.open('shared-content'); await Promise.all( files.map((file, i) => cache.put(`/shared-file-${i}`, new Response(file))) ); // Redirect (with the flag the app checks for a share launch) to a page // that reads the cached files back out. return Response.redirect('/share-handler/view?shared=1', 303); })()); }});3. Read the shared data in the app
Section titled “3. Read the shared data in the app”The redirected page reads whatever the service worker stored — here, the files cached above — and shows them to the user before importing:
async function loadSharedFiles() { const cache = await caches.open('shared-content'); const keys = await cache.keys(); const files = await Promise.all( keys .filter((req) => req.url.includes('/shared-file-')) .map((req) => cache.match(req).then((res) => res.blob())) ); return files;}Where it’s supported
Section titled “Where it’s supported”See the compatibility data for the exact browsers, platforms, and versions that support
share_target:
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Android) | Yes | 76 | low | source | — |
| Edge (Desktop) | Yes | 89 | low | source | 1 |
| Samsung Internet | Yes | 12.0 | low | source | — |
| Firefox (Desktop) | No | — | low | source | 2 |
| Safari (iOS) | No | — | low | source | 3 |
| Safari (macOS) | No | — | low | source | 4 |
Detecting the redirected path and falling back
Section titled “Detecting the redirected path and falling back”This example adds its own shared query parameter to the redirect. The page can detect
that application-defined redirect path and otherwise render its normal view:
function isSharedRedirect() { return new URLSearchParams(location.search).has('shared');}
async function loadSharedFiles() { if (!('caches' in window)) { // No Cache Storage support in this context — nothing to read back. return []; } const cache = await caches.open('shared-content'); const keys = await cache.keys(); return Promise.all( keys .filter((req) => req.url.includes('/shared-file-')) .map((req) => cache.match(req).then((res) => res.blob())) );}
if (isSharedRedirect()) { loadSharedFiles().then(renderSharedFiles);} else { // The app-defined redirect marker is absent — render the normal starting view. renderDefaultView();}Practical checklist
Section titled “Practical checklist”-
share_targetonly takes effect for an installed PWA — an unrelated tab is not offered in the OS share sheet. - File sharing requires
"method": "POST"and"enctype": "multipart/form-data", per MDN — a GET-only target cannot receivefiles. - Prefer a
303redirect after handling the POST; MDN calls this ideal for avoiding repeated POST submissions when the page is refreshed. - Give the redirected route an application-defined marker (e.g. a query parameter) so it can fall back to the normal UI when opened directly.
- Clear or expire cached shared files after reading them so repeated shares don’t accumulate stale data.
Where to go next
Section titled “Where to go next”- Manifest share_target reference — the full
share_targetsyntax, including GET vs POST parameter mapping. - manifest: share_target support — per-browser compatibility data for this manifest member.