Once your web app is installed, two small additions to the manifest change how it feels. Shortcuts appear when someone long-presses your icon and take them straight to a task. A share target puts your app in the system share sheet, so a photo, a link or a piece of text can be sent to it from any other app. Neither needs a framework, and both degrade to nothing where they are not supported.
Where they work
| Shortcuts | Share target | |
|---|---|---|
| Chrome and Edge on Android (installed app) | Yes, long-press the icon | Yes, in the share sheet |
| Chrome and Edge on Windows, ChromeOS | Yes, right-click the taskbar or shelf icon | ChromeOS yes; Windows no |
| Safari on iPhone and iPad | No | No |
| Not installed (a browser tab) | No | No |
Both only exist for an installed app, and both are read from the manifest at install time and when the browser refreshes it (Chrome checks for manifest changes about once a day when the app is opened). After editing, reinstall to see the change straight away.
Shortcuts
{
"shortcuts": [
{
"name": "New order",
"short_name": "Order",
"description": "Start a new order",
"url": "/order/new?source=shortcut",
"icons": [{ "src": "/icons/order-96.png", "sizes": "96x96", "type": "image/png" }]
},
{
"name": "My bookings",
"url": "/bookings?source=shortcut",
"icons": [{ "src": "/icons/bookings-96.png", "sizes": "96x96", "type": "image/png" }]
}
]
}
- Order matters. Launchers show only the first few (Android typically four), so put the most-used task first.
- Every
urlmust be inside the app'sscope, or the shortcut is dropped. - Give each a 96 × 96 icon. Without one the browser uses the app icon, and four identical icons help nobody.
- The
?source=shortcutparameter is optional; it lets your analytics count how often shortcuts are used. - Pick tasks, not pages: "New order" beats "About us".
A share target for text and links
The simplest share target receives a title, text and URL as query parameters on a page you already have:
{
"share_target": {
"action": "/share",
"method": "GET",
"params": { "title": "title", "text": "text", "url": "url" }
}
}
Sharing a link from another app now opens /share?title=…&text=…&url=… in your installed app. Read the values with new URLSearchParams(location.search). Note that many Android apps put the link in text rather than url, so check both.
A share target for files
Files need POST and multipart/form-data:
{
"share_target": {
"action": "/share-file",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "title",
"text": "text",
"files": [{ "name": "media", "accept": ["image/*", "application/pdf"] }]
}
}
}
A static site cannot receive a POST, and it does not need to: the service worker intercepts it before it reaches the network.
// sw.js
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (event.request.method !== 'POST' || url.pathname !== '/share-file') return;
event.respondWith((async () => {
const form = await event.request.formData();
const files = form.getAll('media');
const cache = await caches.open('shared-files');
await Promise.all(files.map((file, i) =>
cache.put('/shared/' + i, new Response(file, { headers: { 'Content-Type': file.type } }))));
// 303: the browser follows with a GET, so a reload does not re-send the share
return Response.redirect('/received?count=' + files.length, 303);
})());
});
The /received page then reads /shared/0, /shared/1… from the cache, shows them, and deletes them when done. Keep the accept list honest: if you accept image/*, your page must cope with a 20 MB camera photo and a HEIC file.
Testing
- In Chrome DevTools open Application → Manifest. It lists your shortcuts and share target and flags errors (a URL outside the scope, a missing icon).
- Install the app on an Android phone, long-press the icon, and try each shortcut.
- Share a link and an image from the Photos app. If your app is missing from the share sheet, the app is not installed or the manifest has not been refreshed yet: uninstall and install again.
- For the file target, open
chrome://inspecton a desktop with the phone connected by USB to watch the service worker receive the POST.
When you need more than the manifest offers
Shortcuts are fixed at install time, and share targets do not exist on iPhone. If you need shortcuts that change per user, or sharing that also works when the app is wrapped for Google Play, the Android packaging route covers both: a packaged app can register dynamic shortcuts and appear in the share sheet for chosen file types.