The ability to share content on the web is a fundamental aspect of the modern online experience. Whether it’s sharing an interesting article, a captivating image, or a noteworthy piece of information, the ability to easily share content is important.
The Web Share API is a JavaScript API that enables web developers to implement native sharing capabilities in web applications. It allows users to share text, URLs, and files directly from a webpage to user-selected sharing targets, which typically include the clipboard, email, contact, messaging applications, or Bluetooth channels.
The navigator.share
method emerges as a valuable tool from the Web Share API that web developers can use to streamline content sharing in their applications. In this article, we will introduce the navigator.share
method, explore advanced features that can be implemented using the API, and look at possible fallback mechanisms for unsupported browsers.
This article assumes that you have an intermediate level of knowledge in working with JavaScript.
The Web Share API is a JavaScript API that is comprised of a navigator
object with two essential methods:
navigator.canShare()
navigator.share()
navigator.canShare
methodThe navigator.canShare
method checks if the data to be shared is shareable before passing it to the navigator.share
method. It ensures that only shareable data is shared, and that the user is not presented with an error message if the data cannot be shared.
This method takes an object as an argument and returns true
if the object contains valid and shareable data:
const data = { title: 'Item 1', text: 'This is the first item', url: 'https://example.com/item1', }; navigator.canShare(data);
If the data cannot be validated, the method returns false
. This can occur for one or more of the following reasons:
navigator.share
methodThe navigator.share
method invokes the native sharing mechanism of the user’s operating system to share specified data to available targets like social media, messaging apps, or other installed applications on the user’s device.
The method takes an object containing the data to be shared using the following property values:
title
(string): The title of the content you want to sharetext
(string): The text or description of the contenturl
(string): The URL of the content you want to sharefiles
(array): The file or files you want to shareconst data = { title: '...', text: '...', url: '...', file: [...] };
The highlighted properties are optional, but at least one of them must be specified.
Additionally, only the share data properties that are understood by the user agent (browser) are assessed. Properties that are unknown to the user agent are ignored. So if your user agent doesn’t recognize the file
property as shareable data and it is declared in your data object, the file property will be ignored.
Once the data parameter is passed to the navigator.share
method and is triggered by the user, using a button or any form of transient activation (more on this later), the method returns a promise that resolves when content has been successfully shared or rejects if the sharing process fails:
navigator.share(data) ... .then(() => { console.log('Sharing was successful'); }) .catch((error) => { console.error('Sharing failed:', error); });
In this example, the method logs the message "Sharing was successful"
if the data has been successfully shared to a target. If the sharing process fails, the method logs the message "Sharing failed"
, followed by the error message.
We mentioned transient activation in the previous paragraph. This is a window
state that indicates a user’s recent interaction, such as pressing a button, moving a mouse, or using a menu. This state is sometimes used as a mechanism to ensure that certain web APIs can only function through user interaction.
This means that APIs requiring transient operations cannot be initiated by a script; they can only be triggered through a UI element’s event handler and by user interaction.
The Web Share API is gated by the transient activation mechanism. If you attempt to trigger the navigator.share
function with a script, you will encounter an exception, specifically the notAllowed
error. This mechanism is put in place to protect end users from malicious actors who may attempt to share harmful content on the user’s device without their knowledge.
Before delving into the specifics, let’s address the issue of browser support. The Web Share API has seen less adoption since its introduction in Chrome 61 for Android six years ago.
Considering the significant number of experimental features that have achieved cross-browser compatibility this year alone, it is surprising that the Web Share API has not made much progress. While it has gained some traction over the years, at the time of writing, it is still not widely supported.
Browsers such as Safari and Edge already have complete compatibility with the feature, while Chrome has partial support. Firefox and Opera, on the other hand, do not appear to have any plans for the feature at this time.
It should go without saying that before using the Web Share API in your projects, it is important to check the client’s browser compatibility. If the browser is not compatible, you should provide a fallback or inform the user that sharing is not available on their device. This will ensure that your site works properly and prevent user experience issues in your application.
Fortunately, creating a fallback for the Web Share API in your applications is also a straightforward process. It involves providing an alternative sharing mechanism to the Web Share API. One common approach is to use the classic sharing links or buttons with the target="blank"
attribute, which allows users to manually share content if their browser is not compatible.
In the markup example below, two buttons with links are provided below the Web Share API button. These buttons serve as a fallback mechanism in the event that the navigator.share
method is not present in the navigator
object:
<main> <h1>Amazing Content</h1> <section> <p> ... </p> </section> <h3>Share this content</h3> <p>Click the button to share this content:</p> <!-- Web Share API Button (Hidden by Default) --> <button id="shareButton" style="display: none"> Share using Web Share API <i class="fa-solid fa-share"></i> </button> <!-- Fallback Sharing Links --> <div id="links" style="display: none"> <a href="https://www.facebook.com/sharer/sharer.php?u=https://example.com" target="_blank" rel="noopener noreferrer" > <button><i class="fa-brands fa-facebook"></i> Facebook</button></a > <a href="https://twitter.com/intent/tweet?text=Check%20out%20this%20awesome%20content&url=https://example.com" target="_blank" rel="noopener noreferrer" > <button><i class="fa-brands fa-twitter"> </i> Twitter</button></a > </div> </main>
Note that both elements are hidden by default. This is done so that either of the elements can be displayed based on the availability of the navigator.share
method on the client’s browser using JavaScript:
if (navigator.share) { // Enable the Web Share API button const shareButton = document.getElementById('shareButton'); shareButton.addEventListener('click', () => { navigator.share({ title: 'Awesome Content', text: 'Check out this awesome content!', url: 'https://example.com', }) .then(() => console.log('Shared successfully')) .catch((error) => console.error('Sharing failed:', error)); }); } else { // If Web Share API is not supported, hide the button const shareButton = document.getElementById('shareButton'); shareButton.style.display = 'none'; }
In this case, the share button will only be displayed if the navigator.share
method is present in the client’s browser. If the method is not present, the sharing links will be displayed instead:
As you can see in the GIF above, the share button is hidden, and the sharing links remain visible in a Firefox browser. However, in Edge, which has full compatibility with the Web Share API, the button is visible.
Alternatively, we can create a fallback using the pop-up window method, which allows users to share content from a pop-up window without leaving the page. This method involves using the window.open()
method and the customShareWindow
parameter:
function popUpShare() { const sharingURL = 'https://example.com/share'; // Open a new pop-up window with the sharing URL. window.open(sharingURL, 'CustomShareWindow', 'width=600, height=400'); } const shareButton = document.getElementById("shareButton"); shareButton.addEventListener('click', popUpShare);
The window.open()
method takes two or three parameters, which include the sharing URL, target, and a comma-separated list of window features that includes the window’s default size, position, and more.
In this example, we set the width
and height
of the pop-up window to 600
and 400
respectively, and set target
to CustomShareWindow
. You can learn more about the window.open()
API on the MDN web docs.
Here’s how our example code will look with the window pop-up fallback implemented:
if (navigator.share) { const title = document.title; const header = document.querySelector("header").innerText; // If Web Share API is supported, enable the Web Share API button const shareButton = document.getElementById("shareButton"); shareButton.style.display = "block"; // Show the button shareButton.addEventListener("click", () => { navigator .share({ title, text: header, url: window.location.href, }) .then(() => console.log("Shared successfully")) .catch((error) => console.error("Sharing failed:", error)); }); } else { // Enable the sharing links button const linkContainer = document.getElementById("links"); linkContainer.style.display = "block"; } function popUpShare(sharingURL) { // Open a new pop-up window with the sharing URL. window.open(sharingURL, 'CustomShareWindow', 'width=500, height=400, left=400, top=100'); } const facebook = document.getElementById("facebook"); const twitter = document.getElementById("twitter"); facebook.addEventListener("click", ()=> popUpShare( "https://www.facebook.com/sharer/sharer.php?u=https://example.com" )); twitter.addEventListener("click", ()=> popUpShare( "https://twitter.com/intent/tweet?text=Check%20out%20this%20awesome%20content&url=https://example.com" ));
Though this method cannot rival the capabilities of the navigator.share
method, it does provide a comparable experience that can enhance your application’s user experience and user retention.
As users interact with your applications, a great deal of information is subject to change, such as data updates, page title and content, and other variables. Sharing outdated content would not be beneficial to your application’s user experience.
Therefore, in most cases, it is necessary to share dynamic content. Sharing dynamic content entails generating a shareable URL that captures the dynamic content’s current state.
Let’s assume that we have a blog site where we want to implement a share button that shares the content of the current page when the navigator.share
method is invoked. It would be counterproductive to create a sharing function and hardcode the URL and header content for each blog post. Instead, we can create just one sharing function with the navigator.share
method and dynamically pass the title and header of each page the users visit to it.
Here is an example of how we can implement this:
function share() { const title = document.title; const header = document.querySelector('header').innerText; navigator.share({ title, text: header, url: window.location.href }); }
In this instance, we used the document
and window
objects to obtain the title, header, and link of the actively focused tab or window on the user’s browser, and then passed them to the navigator.share
method. This ensures that the API will always share the intended content when the user clicks on the share button.
Here’s how our previous example will look with the dynamic content-sharing feature implemented above:
This dynamic-sharing approach isn’t limited to the example above — you can employ the same technique to share a variety of dynamic content.
Due to security reasons, the navigator.share
method doesn’t allow us to specify the target of the shared content or how it appears in the sharing dialog mechanism of the client’s underlying operating system. This is determined by the browser.
However, if you have a progressive web application (PWA) and you want the sharing mechanism of the user’s OS to consider it a possible sharing option, you must meet some criteria set by Chrome.
These criteria are as follows:
Most of the criteria are optional, with the exception of the first one on the list: register as a share target. This is a foundational step in making your PWA a sharing option for the navigator.share
method. It entails registering the PWA as a share target in the web manifest file (manifest.json
) by including the share_target
field.
The share field specifies the action that should be taken when the user shares content with the app. The data types and formats that the PWA can accept can also be defined:
{ "name": "My PWA", "start_url": "/index.html", "display": "standalone", "share_target": { "action": "/share-target", "method": "POST", "enctype": "multipart/form-data", "params": { "title": "text", "text": "text", "url": "url" } } }
The params
object specifies the kind of shared content the PWA can accept. In this case, the app will only accept shared contents with a title, text, and URL.
A share target handler can be a script or service worker that listens for incoming shared data. When content is shared with your app, the handler processes the shared data and performs the necessary actions. Here’s a simplified example of a target handler:
// Service worker or JavaScript code in your PWA self.addEventListener('sharetarget', (event) => { // Extract shared data const title = event.data.title; const text = event.data.text; const url = event.data.url; // Process the shared data // You can perform actions like displaying the shared content or saving it. console.log(`Shared content: Title - ${title}, Text - ${text}, URL - ${url}`); });
The navigator.share
method doesn’t have a built-in feature that allows us to perform batch sharing by default. Therefore, if you need to share multiple items at once, the method in its original form will not be very helpful.
However, there are methods to implement such a feature. One approach is to store the provided data in an array, iterate through it, and sequentially pass each item in the array to the navigator.share
method:
const data = [ { title: 'Item 1', text: 'This is the first item', url: 'https://example.com/item1', }, { title: 'Item 2', text: 'This is the second item', url: 'https://example.com/item2', }, ]; // Function to share items sequentially async function shareItems() { for (const item of data) { try { await navigator.share(item); console.log(`Shared successfully: ${item.title}`); } catch (error) { console.error(`Sharing failed: ${item.title}`, error); } } } shareButton.addEventListener('click', shareItems);
Although this approach might appear valid, it won’t perform as intended. As mentioned in the previous section, the navigator.share
method requires transient activation to operate.
Consequently, it’s not possible to share multiple items collectively. While the initial button click will successfully share the first item in the array, the subsequent items will trigger errors because they would be invoked without user interaction:
The initial item in the array is successfully shared, but the subsequent item encounters sharing failure. This is because the navigator.share
method cannot be triggered by a script alone.
A potential solution would involve creating buttons that appear sequentially with every loop iteration. Upon clicking these buttons, the navigator.share
method could be invoked for each item in the array:
const shareButton = document.getElementById("shareButton"); const shareContainer = document.getElementById('shareContainer'); const data = [ { title: 'Item 1', text: 'This is the first item', url: 'https://example.com/item1', }, { title: 'Item 2', text: 'This is the second item', url: 'https://example.com/item2', }, ]; function shareSequentially(index) { if (index < shareData.length) { const item = data[index]; const button = document.createElement('button'); button.textContent = `Share ${item.title}`; button.addEventListener('click', () => shareItem(item, button, index)); shareContainer.appendChild(button); } else { console.log('All items shared'); } } function shareItem(item, button, index) { try { navigator.share(item).then(() => { console.log(`Shared successfully: ${item.title}`); }); } catch (error) { console.error(`Sharing failed: ${item.title}`, error); } finally { button.parentNode.removeChild(button); shareSequentially(index + 1); } } // Attach a click event listener to the share button to create share buttons shareButton.addEventListener('click', () => shareSequentially(0));
In the code above, we create a shareSequentially
function that takes an index
parameter to track the current item being shared. Inside the function, a share button is created for the current item. The click event handler for each button is set to call the shareItem
function, passing the current item, the button, and the index.
The shareItem
function invokes the navigator.share
method with the current item. After sharing the item (whether successfully or not), it removes the button from the DOM and proceeds to share the next item in the array, after the shareSequentially
function has created and rendered a new button.
The corresponding markup code will be as follows:
<p>Click the button to share items:</p> <button id="shareButton"> Share Items </button> <div id="shareContainer"> <!-- Share buttons will be added here --> </div>
This approach is a bit hacky, but it gets the job done and is the closest we’ll get to batch sharing with the navigator.share
method.
In as much as we’d like it to be, the navigator.share
method is not a universally applicable solution for web content sharing and is unlikely to become one anytime soon due to the security risks associated with the API.
However, given the myriad of fallbacks that can be implemented for the API, adopting it should not be an issue. Additionally, the support that the API does provide can greatly improve the user experience in your applications.
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "An advanced guide to the Web Share API and navigator.share()"
Amazing article! Can you share the demo and source code 🙂
Because my browser is supporting the API but not working due to permissions.