[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-web-share-api-native-sharing":44,"search-suggestions":253,"related-web-share-api-native-sharing":292,"code:html:true:1vtdimn":417,"code:js:true:1no3hkb":418,"code:js:true:1lv9xuy":419,"code:js:true:2iqdrz":420,"code:js:true:qsfwje":421,"code:js:true:1xqku0e":422,"comments-01a06c11-0cae-7489-8373-0e3cfee9f0fb":423},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":236,"seo":237,"translationGroupId":240,"series":48,"podcastUrl":48,"verticalId":5,"thread":241,"assessments":243,"translations":249,"quiz":251},"01a06c11-0cae-7489-8373-0e3cfee9f0fb","web-share-api-native-sharing","Those Share Buttons Are Guessing What's On My Phone",null,"Every blog footer hardcodes the same row of share icons — X, Facebook, copy link — and none of them know what's actually installed on your phone. The Web Share API hands the job to the OS instead, and it can do something a custom row never could: share an actual file.","\u002Fmedia\u002Fcovers\u002Fweb-share-api-native-sharing.png",6,"2026-09-09T11:00:52.000Z",52,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"javascript","JavaScript",[64,66,69,72],{"slug":61,"name":65,"color":48},"Javascript",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"browser","Browser",{"slug":73,"name":74,"color":48},"apis","Apis",{"assessments":76},1,{"slug":46,"title":78},"Web Share API — interactive playground",{"blocks":80,"version":76},[81,85,88,91,95,98,104,107,110,118,121,124,127,130,133,136,139,144,147,150,153,156,160,163,167,170,174,177,180,184,187,190,194,197,200,203,206,209,212,215,218,221,224,227,230],{"id":82,"html":83,"type":84},"b1","\u003Cp>Scroll to the bottom of almost any blog post and you&#39;ll find the same five icons: a bird that got rebranded two years ago, a lowercase &quot;f&quot; in a circle, a chain-link for &quot;copy URL,&quot; maybe an envelope. Someone built that row once, for the apps that mattered at the time, and it has been quietly lying to every reader since.\u003C\u002Fp>","paragraph",{"id":86,"html":87,"type":84},"b2","\u003Cp>It doesn&#39;t know if you have Twitter — sorry, X — installed. It doesn&#39;t know you&#39;d rather send this to yourself on WhatsApp, or drop it in the Notes app, or AirDrop it to the laptop next to you. It just shows you five guesses and hopes one lands.\u003C\u002Fp>",{"id":89,"html":90,"type":84},"b3","\u003Cp>There&#39;s a three-line replacement that stops guessing and asks your phone directly. Most developers have never reached for it, because for years it only half-worked. That&#39;s no longer true — and it can do something the icon row structurally can&#39;t.\u003C\u002Fp>",{"id":92,"html":93,"text":93,"type":94,"level":31},"b4","The row that never keeps up","heading",{"id":96,"html":97,"type":84},"b5","\u003Cp>The obvious fix, the one every project reaches for first, is to keep adding icons. Someone requests a WhatsApp button. Then Threads. Then someone on the team points out half your readers are on Reddit, so that&#39;s an icon too. Each one needs its own share-URL format memorized or copy-pasted from a Stack Overflow answer:\u003C\u002Fp>",{"id":99,"code":100,"type":101,"language":102,"highlight":103},"b6","\u003Ca href=\"https:\u002F\u002Ftwitter.com\u002Fintent\u002Ftweet?text=...&url=...\">Share on X\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fwww.facebook.com\u002Fsharer\u002Fsharer.php?u=...\">Share on Facebook\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fapi.whatsapp.com\u002Fsend?text=...\">Share on WhatsApp\u003C\u002Fa>","code","html",[],{"id":105,"html":106,"type":84},"b7","\u003Cp>This works, in the sense that clicking it opens something. But it&#39;s a maintenance tax with no ceiling — a new network shows up, you add a link; a network dies or rebrands, you go find every place you hardcoded its old URL scheme. And no matter how many icons you add, the row is finite while the list of apps on someone&#39;s phone isn&#39;t.\u003C\u002Fp>",{"id":108,"html":109,"text":109,"type":94,"level":31},"b8","Where it actually falls apart",{"id":111,"type":112,"items":113,"ordered":18},"b9","list",[114,115,116,117],"\u003Cstrong>It can only ever be a link.\u003C\u002Fstrong> Every one of those \u003Ccode>&lt;a&gt;\u003C\u002Fcode> tags is built to hand off a URL. There&#39;s no icon for &quot;share the PDF I just generated&quot; or &quot;share this canvas as an image&quot; — a link-based row has no mechanism for that at all.","\u003Cstrong>It never matches the reader&#39;s actual apps.\u003C\u002Fstrong> Messages, Notes, AirDrop, Slack, a note-taking app, a second messenger — none of them show up, because the row was hardcoded to the networks someone thought of in advance.","\u003Cstrong>It&#39;s a tracking and consent question you didn&#39;t sign up for.\u003C\u002Fstrong> Some of those share-URL patterns silently hand the destination site data about the page you&#39;re on before the reader has clicked anything.","\u003Cstrong>It ages badly, publicly.\u003C\u002Fstrong> A dead rebrand icon at the bottom of a post is a small, visible signal that nobody&#39;s touched this part of the site in years.",{"id":119,"html":120,"text":120,"type":94,"level":31},"b10","Ask the OS instead",{"id":122,"html":123,"type":84},"b11","\u003C!-- playground:start -->",{"id":125,"html":126,"text":126,"type":94,"level":31},"b12","🎮 Try it yourself",{"id":128,"html":129,"type":84},"b13","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweb-share-api-native-sharing\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":131,"html":132,"type":84},"b14","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":134,"html":135,"type":84},"b15","\u003C!-- playground:end -->",{"id":137,"html":138,"type":84},"b16","\u003Cp>The Web Share API skips the guessing entirely. Instead of a fixed row, you hand the browser a small object and let the operating system show the reader \u003Cem>their\u003C\u002Fem> actual share sheet — whatever&#39;s really installed:\u003C\u002Fp>",{"id":140,"code":141,"type":101,"language":142,"highlight":143},"b17","async function shareThisPage() {\n  try {\n    await navigator.share({\n      title: document.title,\n      text: \"Worth a read:\",\n      url: location.href,\n    });\n  } catch (err) {\n    console.error(`${err.name}: ${err.message}`);\n  }\n}\n\nshareButton.addEventListener(\"click\", shareThisPage);","js",[],{"id":145,"html":146,"type":84},"b18","\u003Cp>\u003Ccode>navigator.share()\u003C\u002Fcode> returns a promise that resolves once the reader picks something from the sheet — Messages, WhatsApp, AirDrop, whatever they actually have. It needs two things to work: a secure context (HTTPS or localhost) and a real user gesture, so it has to be called from inside a click handler, not on page load or after some unrelated timer fires.\u003C\u002Fp>",{"id":148,"html":149,"text":149,"type":94,"level":31},"b19","The part the icon row can never do",{"id":151,"html":152,"type":84},"b20","\u003Cp>Here&#39;s the part that isn&#39;t just nicer UX — it&#39;s a capability gap. \u003Ccode>navigator.share()\u003C\u002Fcode> also accepts a \u003Ccode>files\u003C\u002Fcode> array, and once a browser supports it, you can hand the OS an actual \u003Ccode>File\u003C\u002Fcode> object: an image you generated on a \u003Ccode>&lt;canvas&gt;\u003C\u002Fcode>, a PDF you built client-side, a screenshot. No \u003Ccode>&lt;a&gt;\u003C\u002Fcode> tag has ever been able to do that; a link can only point at something, never carry it.\u003C\u002Fp>",{"id":154,"html":155,"type":84},"b21","\u003Cp>Because file support varies by platform and browser in ways JavaScript can&#39;t enumerate up front, check first with \u003Ccode>canShare()\u003C\u002Fcode> — it&#39;s synchronous, so you get a plain boolean back with no promise to unwrap:\u003C\u002Fp>",{"id":157,"code":158,"type":101,"language":142,"highlight":159},"b22","const shareData = { files: [imageFile], title: \"Generated chart\" };\n\nif (navigator.canShare?.(shareData)) {\n  await navigator.share(shareData);\n} else {\n  \u002F\u002F Fall back to a download link — don't guess at what the OS will accept.\n}",[],{"id":161,"html":162,"type":84},"b23","\u003Cp>Don&#39;t try to hardcode your own list of &quot;safe&quot; MIME types for this. The spec leaves the exact allowed file types to the browser and OS, and it&#39;s shifted over time — \u003Ccode>canShare()\u003C\u002Fcode> is the one place that question gets answered correctly.\u003C\u002Fp>",{"id":164,"html":165,"text":166,"type":94,"level":31},"b24","The gotcha: canceling isn&#39;t a failure","The gotcha: canceling isn't a failure",{"id":168,"html":169,"type":84},"b25","\u003Cp>Every \u003Ccode>navigator.share()\u003C\u002Fcode> call you&#39;ll find in a hurry looks like this:\u003C\u002Fp>",{"id":171,"code":172,"type":101,"language":142,"highlight":173},"b26","navigator.share(data).catch(() => {\n  showToast(\"Sharing failed. Please try again.\");\n});",[],{"id":175,"html":176,"type":84},"b27","\u003Cp>Try it yourself: click share, then just tap outside the sheet to dismiss it without picking anything. That toast fires anyway — for a reader who did nothing wrong.\u003C\u002Fp>",{"id":178,"html":179,"type":84},"b28","\u003Cp>Canceling the share sheet rejects the promise with an \u003Ccode>AbortError\u003C\u002Fcode>. That&#39;s not a bug and not a failure; it&#39;s the single most common outcome, because plenty of people open the sheet just to see what&#39;s there. Treating every rejection as an error means you&#39;re apologizing to readers for a thing they chose to do on purpose:\u003C\u002Fp>",{"id":181,"code":182,"type":101,"language":142,"highlight":183},"b29","navigator.share(data).catch((err) => {\n  if (err.name === \"AbortError\") return; \u002F\u002F they just closed the sheet — not an error\n  showToast(\"Sharing failed. Please try again.\");\n});",[],{"id":185,"html":186,"text":186,"type":94,"level":31},"b30","Ship it without stranding anyone",{"id":188,"html":189,"type":84},"b31","\u003Cp>Feature-detect before you touch any of this, and keep a fallback path — this is the one place the old row still earns its keep:\u003C\u002Fp>",{"id":191,"code":192,"type":101,"language":142,"highlight":193},"b32","if (navigator.share) {\n  shareButton.addEventListener(\"click\", shareThisPage);\n} else {\n  \u002F\u002F No native share sheet here — show your copy-link \u002F mailto row instead.\n  legacyShareRow.hidden = false;\n}",[],{"id":195,"html":196,"type":84},"b33","\u003Cp>Support is solid on mobile Safari and Chrome, and it&#39;s since spread to desktop Chrome and Edge too, where it opens the OS&#39;s own share panel. Firefox has never shipped \u003Ccode>navigator.share()\u003C\u002Fcode> — check caniuse.com for the current picture before you rely on it, and keep the fallback branch rather than assuming everyone gets the native sheet. One more thing worth knowing if you&#39;re sharing a generated file: the &quot;user gesture&quot; the API needs can expire if you spend too long generating that file before calling \u003Ccode>share()\u003C\u002Fcode> — keep the gap between the click and the call as short as you can.\u003C\u002Fp>",{"id":198,"html":199,"text":199,"type":94,"level":31},"b34","The row, retired",{"id":201,"html":202,"type":84},"b35","\u003Cp>Next time you&#39;re about to paste in another network&#39;s share URL, ask what you&#39;re actually trying to do: give the reader \u003Cem>a\u003C\u002Fem> way to share, or give them \u003Cem>their\u003C\u002Fem> way. The five-icon row was always a stand-in for an answer the OS could give directly — it just took the platform a while to offer it.\u003C\u002Fp>",{"id":204,"html":205,"type":84},"b36","\u003Cp>Still got a Twitter bird in your footer? Go check — I&#39;ll bet it&#39;s still pointing at the old URL scheme.\u003C\u002Fp>",{"id":207,"html":208,"type":84},"b37","\u003C!-- quiz:start -->",{"id":210,"html":211,"text":211,"type":94,"level":31},"b38","🧠 Test yourself",{"id":213,"html":214,"type":84},"b39","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweb-share-api-native-sharing\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":216,"html":217,"type":84},"b40","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":219,"html":220,"type":84},"b41","\u003C!-- quiz:end -->",{"id":222,"type":223},"b42","divider",{"id":225,"html":226,"type":84},"b43","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":228,"html":229,"type":84},"b44","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":231,"type":112,"items":232,"ordered":18},"b45",[233,234,235],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","Scroll to the bottom of almost any blog post and you'll find the same five icons: a bird that got rebranded two years ago, a lowercase \"f\" in a circle, a chain-link for \"copy URL,\" maybe an envelope. Someone built that row once, for the apps that mattered at the time, and it has been quietly lying to every reader since.\n\nIt doesn't know if you have Twitter — sorry, X — installed. It doesn't know you'd rather send this to yourself on WhatsApp, or drop it in the Notes app, or AirDrop it to the laptop next to you. It just shows you five guesses and hopes one lands.\n\nThere's a three-line replacement that stops guessing and asks your phone directly. Most developers have never reached for it, because for years it only half-worked. That's no longer true — and it can do something the icon row structurally can't.\n\n## The row that never keeps up\n\nThe obvious fix, the one every project reaches for first, is to keep adding icons. Someone requests a WhatsApp button. Then Threads. Then someone on the team points out half your readers are on Reddit, so that's an icon too. Each one needs its own share-URL format memorized or copy-pasted from a Stack Overflow answer:\n\n```html\n\u003Ca href=\"https:\u002F\u002Ftwitter.com\u002Fintent\u002Ftweet?text=...&url=...\">Share on X\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fwww.facebook.com\u002Fsharer\u002Fsharer.php?u=...\">Share on Facebook\u003C\u002Fa>\n\u003Ca href=\"https:\u002F\u002Fapi.whatsapp.com\u002Fsend?text=...\">Share on WhatsApp\u003C\u002Fa>\n```\n\nThis works, in the sense that clicking it opens something. But it's a maintenance tax with no ceiling — a new network shows up, you add a link; a network dies or rebrands, you go find every place you hardcoded its old URL scheme. And no matter how many icons you add, the row is finite while the list of apps on someone's phone isn't.\n\n## Where it actually falls apart\n\n- **It can only ever be a link.** Every one of those `\u003Ca>` tags is built to hand off a URL. There's no icon for \"share the PDF I just generated\" or \"share this canvas as an image\" — a link-based row has no mechanism for that at all.\n- **It never matches the reader's actual apps.** Messages, Notes, AirDrop, Slack, a note-taking app, a second messenger — none of them show up, because the row was hardcoded to the networks someone thought of in advance.\n- **It's a tracking and consent question you didn't sign up for.** Some of those share-URL patterns silently hand the destination site data about the page you're on before the reader has clicked anything.\n- **It ages badly, publicly.** A dead rebrand icon at the bottom of a post is a small, visible signal that nobody's touched this part of the site in years.\n\n## Ask the OS instead\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweb-share-api-native-sharing\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\nThe Web Share API skips the guessing entirely. Instead of a fixed row, you hand the browser a small object and let the operating system show the reader *their* actual share sheet — whatever's really installed:\n\n```js\nasync function shareThisPage() {\n  try {\n    await navigator.share({\n      title: document.title,\n      text: \"Worth a read:\",\n      url: location.href,\n    });\n  } catch (err) {\n    console.error(`${err.name}: ${err.message}`);\n  }\n}\n\nshareButton.addEventListener(\"click\", shareThisPage);\n```\n\n`navigator.share()` returns a promise that resolves once the reader picks something from the sheet — Messages, WhatsApp, AirDrop, whatever they actually have. It needs two things to work: a secure context (HTTPS or localhost) and a real user gesture, so it has to be called from inside a click handler, not on page load or after some unrelated timer fires.\n\n## The part the icon row can never do\n\nHere's the part that isn't just nicer UX — it's a capability gap. `navigator.share()` also accepts a `files` array, and once a browser supports it, you can hand the OS an actual `File` object: an image you generated on a `\u003Ccanvas>`, a PDF you built client-side, a screenshot. No `\u003Ca>` tag has ever been able to do that; a link can only point at something, never carry it.\n\nBecause file support varies by platform and browser in ways JavaScript can't enumerate up front, check first with `canShare()` — it's synchronous, so you get a plain boolean back with no promise to unwrap:\n\n```js\nconst shareData = { files: [imageFile], title: \"Generated chart\" };\n\nif (navigator.canShare?.(shareData)) {\n  await navigator.share(shareData);\n} else {\n  \u002F\u002F Fall back to a download link — don't guess at what the OS will accept.\n}\n```\n\nDon't try to hardcode your own list of \"safe\" MIME types for this. The spec leaves the exact allowed file types to the browser and OS, and it's shifted over time — `canShare()` is the one place that question gets answered correctly.\n\n## The gotcha: canceling isn't a failure\n\nEvery `navigator.share()` call you'll find in a hurry looks like this:\n\n```js\nnavigator.share(data).catch(() => {\n  showToast(\"Sharing failed. Please try again.\");\n});\n```\n\nTry it yourself: click share, then just tap outside the sheet to dismiss it without picking anything. That toast fires anyway — for a reader who did nothing wrong.\n\nCanceling the share sheet rejects the promise with an `AbortError`. That's not a bug and not a failure; it's the single most common outcome, because plenty of people open the sheet just to see what's there. Treating every rejection as an error means you're apologizing to readers for a thing they chose to do on purpose:\n\n```js\nnavigator.share(data).catch((err) => {\n  if (err.name === \"AbortError\") return; \u002F\u002F they just closed the sheet — not an error\n  showToast(\"Sharing failed. Please try again.\");\n});\n```\n\n## Ship it without stranding anyone\n\nFeature-detect before you touch any of this, and keep a fallback path — this is the one place the old row still earns its keep:\n\n```js\nif (navigator.share) {\n  shareButton.addEventListener(\"click\", shareThisPage);\n} else {\n  \u002F\u002F No native share sheet here — show your copy-link \u002F mailto row instead.\n  legacyShareRow.hidden = false;\n}\n```\n\nSupport is solid on mobile Safari and Chrome, and it's since spread to desktop Chrome and Edge too, where it opens the OS's own share panel. Firefox has never shipped `navigator.share()` — check caniuse.com for the current picture before you rely on it, and keep the fallback branch rather than assuming everyone gets the native sheet. One more thing worth knowing if you're sharing a generated file: the \"user gesture\" the API needs can expire if you spend too long generating that file before calling `share()` — keep the gap between the click and the call as short as you can.\n\n## The row, retired\n\nNext time you're about to paste in another network's share URL, ask what you're actually trying to do: give the reader *a* way to share, or give them *their* way. The five-icon row was always a stand-in for an answer the OS could give directly — it just took the platform a while to offer it.\n\nStill got a Twitter bird in your footer? Go check — I'll bet it's still pointing at the old URL scheme.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweb-share-api-native-sharing\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":238,"description":239},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweb-share-api-native-sharing","Every blog footer hardcodes the same row of share icons — X, Facebook, copy link — and none of them know what's actually installed on your phone. The Web Share API hands the job to","01a06c11-0cae-7489-8373-10e842acc254",{"id":242,"locked":18},"01a06c11-0cd4-7584-82ac-49e6cf0e28d8",[244],{"id":245,"slug":46,"title":246,"_count":247},"01a06c11-0d4e-7784-9172-73d3c7a0f83a","The Web Share API",{"questions":248},7,[250],{"locale":13,"slug":46},{"id":245,"slug":46,"title":246,"_count":252,"questionCount":248},{"questions":248},[254,256,258,262,266,270,274,278,282,283,286,289],{"slug":67,"name":68,"articles":255},87,{"slug":61,"name":65,"articles":257},71,{"slug":259,"name":260,"articles":261},"frontend","Frontend",69,{"slug":263,"name":264,"articles":265},"css","Css",31,{"slug":267,"name":268,"articles":269},"tutorial","Tutorial",19,{"slug":271,"name":272,"articles":273},"performance","Performance",12,{"slug":275,"name":276,"articles":277},"typescript","Typescript",11,{"slug":279,"name":280,"articles":281},"react","React",10,{"slug":70,"name":71,"articles":248},{"slug":284,"name":285,"articles":51},"grammar","Grammar",{"slug":102,"name":287,"articles":288},"Html",5,{"slug":290,"name":291,"articles":288},"node","Node",{"items":293,"meta":416},[294,306,324,342,361,380,398],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":295,"likeCount":19,"commentCount":19,"author":296,"vertical":297,"topic":298,"tags":299,"_count":304,"playground":305,"hasQuiz":17,"hasPlayground":17},53,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[300,301,302,303],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":307,"slug":308,"title":309,"subtitle":48,"excerpt":310,"coverUrl":311,"locale":13,"readingMinutes":273,"publishedAt":312,"viewCount":313,"likeCount":19,"commentCount":19,"author":314,"vertical":315,"topic":316,"tags":317,"_count":322,"playground":323,"hasQuiz":17,"hasPlayground":17},"01a018ca-67d5-7020-bce5-9816c38a7a54","weekly-web-workers","Web Workers in JavaScript: The Complete Guide","Learn JavaScript Web Workers step by step: message passing, transferable objects, error handling, and a copy-paste cheat sheet for offloading work.","\u002Fmedia\u002Fcovers\u002Fweekly-web-workers.png","2026-09-07T12:03:05.323Z",106,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[318,319,320,321],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":267,"name":268,"color":48},{"slug":271,"name":272,"color":48},{"assessments":76},{"slug":308},{"id":325,"slug":326,"title":327,"subtitle":48,"excerpt":328,"coverUrl":329,"locale":13,"readingMinutes":288,"publishedAt":330,"viewCount":331,"likeCount":19,"commentCount":19,"author":332,"vertical":333,"topic":334,"tags":335,"_count":340,"playground":341,"hasQuiz":17,"hasPlayground":17},"01a05cb5-e732-76ca-8865-14b97c64f2ba","screen-wake-lock-api-keep-screen-on","Your Recipe App Is Hiding a Silent Video","Cooking sites, workout timers, and boarding-pass pages used to autoplay an invisible, muted, looping video for one job only: stopping your phone from locking its screen. The Screen Wake Lock API replaces that hack with three lines of JavaScript — and it comes with one gotcha the docs bury.","\u002Fmedia\u002Fcovers\u002Fscreen-wake-lock-api-keep-screen-on.png","2026-09-06T10:37:22.891Z",117,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[336,337,338,339],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":326},{"id":343,"slug":344,"title":345,"subtitle":48,"excerpt":346,"coverUrl":347,"locale":13,"readingMinutes":348,"publishedAt":349,"viewCount":350,"likeCount":19,"commentCount":19,"author":351,"vertical":352,"topic":353,"tags":354,"_count":359,"playground":360,"hasQuiz":17,"hasPlayground":17},"01a0632c-7248-73e3-9951-e8c6eaa578c7","react-weekly-compiler-memoization","React Compiler 1.0: What useMemo You Can Delete","React Compiler 1.0 is stable. Here's the re-render model it automates, which useMemo\u002FuseCallback\u002Fmemo calls you can delete, and what still needs you.","\u002Fmedia\u002Fcovers\u002Freact-weekly-compiler-memoization.png",13,"2026-09-05T10:17:49.849Z",168,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":279,"name":280},[355,356,357,358],{"slug":279,"name":280,"color":48},{"slug":61,"name":65,"color":48},{"slug":267,"name":268,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":344},{"id":362,"slug":363,"title":364,"subtitle":48,"excerpt":365,"coverUrl":366,"locale":13,"readingMinutes":367,"publishedAt":368,"viewCount":369,"likeCount":19,"commentCount":19,"author":370,"vertical":371,"topic":372,"tags":373,"_count":378,"playground":379,"hasQuiz":17,"hasPlayground":17},"01a057f2-68b2-7521-a373-0ea70eae6f55","page-visibility-api-tab-aware-timers","Your Timer Doesn't Know You Tabbed Away","window blur feels like the obvious way to detect an idle tab, and it's wrong in ways that only show up in production. The Page Visibility API answers the actual question — can anyone see this? — so polling, timers, and video stop wasting work the moment nobody's looking.","\u002Fmedia\u002Fcovers\u002Fpage-visibility-api-tab-aware-timers.png",4,"2026-09-05T10:17:19.010Z",135,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[374,375,376,377],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":271,"name":272,"color":48},{"assessments":76},{"slug":363},{"id":381,"slug":382,"title":383,"subtitle":48,"excerpt":384,"coverUrl":385,"locale":13,"readingMinutes":288,"publishedAt":386,"viewCount":387,"likeCount":19,"commentCount":19,"author":388,"vertical":389,"topic":390,"tags":391,"_count":396,"playground":397,"hasQuiz":17,"hasPlayground":17},"01a04d81-723b-725d-9e99-7e11d03aa56a","clipboard-writetext-focus-bug","The await That Silently Breaks navigator.clipboard.writeText()","navigator.clipboard.writeText() only works while the document is focused and the user gesture is still fresh. Any await before the call can let both expire, so a copy button fails with an uncaught NotAllowedError and nobody ever sees why.","\u002Fmedia\u002Fcovers\u002Fclipboard-writetext-focus-bug.png","2026-09-03T17:23:32.218Z",218,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[392,393,394,395],{"slug":67,"name":68,"color":48},{"slug":61,"name":65,"color":48},{"slug":70,"name":71,"color":48},{"slug":259,"name":260,"color":48},{"assessments":76},{"slug":382},{"id":399,"slug":400,"title":401,"subtitle":48,"excerpt":402,"coverUrl":403,"locale":13,"readingMinutes":288,"publishedAt":404,"viewCount":405,"likeCount":19,"commentCount":19,"author":406,"vertical":407,"topic":408,"tags":409,"_count":414,"playground":415,"hasQuiz":17,"hasPlayground":17},"01a0499d-bb9f-709b-9e8a-9c426ae95530","declarative-shadow-dom-ssr-web-components","Shadow DOM Needed JavaScript to Exist. Now It Doesn't.","attachShadow() is a JavaScript-only API with no HTML serialization, so a server-rendered web component ships an empty host element until the client hydrates it. Declarative Shadow DOM attaches the real shadow root during HTML parsing — zero JS required.","\u002Fmedia\u002Fcovers\u002Fdeclarative-shadow-dom-ssr-web-components.png","2026-09-02T10:57:52.645Z",204,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[410,411,412,413],{"slug":67,"name":68,"color":48},{"slug":61,"name":65,"color":48},{"slug":102,"name":287,"color":48},{"slug":259,"name":260,"color":48},{"assessments":76},{"slug":400},{"page":76,"perPage":248,"total":257,"totalPages":277},"\u003Cdiv class=\"shj shj-lang-html shj-multiline\" data-lang=\"html\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">a\u003C\u002Fspan> \u003Cspan class=\"shj-class\">href\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"https:\u002F\u002Ftwitter.com\u002Fintent\u002Ftweet?text=...&#38;url=...\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>Share on X\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">a\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">a\u003C\u002Fspan> \u003Cspan class=\"shj-class\">href\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"https:\u002F\u002Fwww.facebook.com\u002Fsharer\u002Fsharer.php?u=...\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>Share on Facebook\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">a\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">a\u003C\u002Fspan> \u003Cspan class=\"shj-class\">href\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"https:\u002F\u002Fapi.whatsapp.com\u002Fsend?text=...\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>Share on WhatsApp\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">a\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">shareThisPage\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">try\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">share\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      title\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> document\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>title\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n      text\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Worth a read:\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n      url\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> location\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>href\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">catch\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">error\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>name\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">: \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>message\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\nshareButton\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> shareThisPage\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> shareData \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> files\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>imageFile\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> title\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Generated chart\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">canShare\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">?.\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>shareData\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">share\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>shareData\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">else\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F Fall back to a download link — don't guess at what the OS will accept.\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">share\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>data\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">catch\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">showToast\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Sharing failed. Please try again.\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">share\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>data\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">catch\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>name \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"AbortError\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F they just closed the sheet — not an error\n\u003C\u002Fspan>  \u003Cspan class=\"shj-func\">showToast\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Sharing failed. Please try again.\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>navigator\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>share\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  shareButton\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"click\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> shareThisPage\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">else\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F No native share sheet here — show your copy-link \u002F mailto row instead.\n\u003C\u002Fspan>  legacyShareRow\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>hidden \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":424},[]]