Hands on Web Share API
Tags: webdev, javascript, programming, firstpost
Published: 2021-08-19 13:26:48

Hello world, welcome to my first post!
In this one, I will explain what the Web Share API is and what you can do with it. If you didn't work it yet, maybe you give it a try in your current or next project.

## What is the Web Share API?
Let's say, you want to share some page specific data from your website to some social media platforms and maybe messengers.
The Web Share API give web developers the power to use the native share mechanisms, that we all know from native applications (e.g. if you click the share button in Safari on the bottom center). It's one of these cool new APIs, that give the web more capability and push the keyword "Progressive Web App" a little more. If you haven't got any idea what the hell I'm talking about, here is a picture for you:

![Web Share API - native dialog on iOS after clicking the button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53gosz0qo8cdhg3vox4i.png)*Web Share API - native dialog on iOS after clicking the button*

---

## Why should I use the Web Share API?
You may ask yourself why you should use this API, because you already have enough share possibilities like WhatsApp, Facebook, Twitter, LinkedIn etc. within your application. That's fine, but think about the Web Share API as the cool new kid on the block, which also makes it much easier for you to implement these share possibilities.

### Without the Web Share API
In this case, we should have to provide a link/button for every social media/messenger platform. That means, we have to take care of each link separately. That also implies to maintain these links, because they could change in the future.

### With the Web Share API
In this case, we will just have one button to click on. After clicking this button, the native dialog will be shown. One advantage of this native feature is, that it is known by the users. Another advantage (I think a bigger one) is, that if there is a new social media platform or native share feature (in the OS) it's directly implemented! All done by the native mechanism! Yeah! 🎉

---

## How can I use the Web Share API?
Like any other new cool Browser API out there, it's asynchronous. That means we have to work with Promises (if you are not familiar with Promises, I'm sure you will find a good article out there). In this case, we will call our `navigator.share()` function, which will return a promise:

```javascript
const sharePromise = navigator.share(shareData);
```

Okay, maybe we need some more context to have a good example.
 
Let's start with the `share()` function, that will take the data object (`shareData`) as a parameter. The result of this call, will be the native share dialog with some share targets, depending on the data that was thrown in. A **share target** is a possible option, that is displayed to the user in the dialog. This could be a contact (via WhatsApp, Telegram etc.), native applications or built-in service (e.g. "Copy to clipboard"). To make it clear here, you can't decide which share targets should be shown to the user, they were provided by the user agent.

So let's start with the most secret part ... the `shareData`. Honestly, it's just an object that **can** contain the following members:

* title
* text
* url
* files

In the future there could be some more members, but this is the current state. It's important that not all data members have to be provided, but at least one member should be inside.

The user agent will take care of the `shareData` and converts these in a suitable format for the different targets. This could include merging or discarding some members. But this magic is done behind the scene, we can just take a drink and enjoy the beautiful interface. 🍹
The share targets that you will see on the native dialog depends on the `shareData` payload. Anyway, let's dive into the different members of the `shareData` object.

### Title member
This member is a string and contains the title of the document that is shared. In my tests, I couldn't find it anyway. So in most cases, this member is not being displayed or used on most targets.

### Url member
The url member is just a simple pure string URL that refers to a resource that should be shared. This could be an absolute or relative URL. If you provide a relative URL it will automatically update the url like a _href_ attribute.

Pro Tip: If you just provide an empty string as url, then it will automatically reffers to the current page.

### Text member
The text member is also a string option, that allows you to provide a body of the message for the shared data. This member is often use by the share targets (e.g. email body text).

### Everything in action
You might think, why didn't we cover the files member. Please keep cool and take a breath, we will cover it in a couple of minutes. But I think now it's time for some code. Finally 🎉

Let's say we have a fancy button on our page, where we just want to share some data related to the current page. The JS code would look something like this:

```javascript
// our share button in the markup
const shareButton = document.getElementById('share');

// create share data object
const shareData = {
  title: 'Web Share API',
  text: 'The best way to share your data.',
  url: '' // reffers to the current page
};

// click event handler is necessary to call navigator.share()
shareButton.addEventListener('click', async () => {
  // does the browser supports the feature?
  if(navigator.share) {
    try {
      await navigator.share(shareData);
      console.log('🥳 Shared via API.');
    } catch(error) {
      console.log(`😢 ${error}`);
    }
  } else {
    // you could do a fallback solution here ... 
    console.log('😢 Your browser does not support the web share api.')
  }
})
```

Now let's go through the code step by step. At first, we create an object and add some members to it, in this case `title`, `text` and `url`. Then we add an event listener to our `shareButton`. In the event handler, we check for `navigator.share`, because we want to check if the browser supports the Web Share API. If not, it will return `undefined` and the else branch will be executed. There could be a fallback solution for all browsers that are not supporting this functionality. At least this would be the best, if we think about progressive enhancement. But in this post we want to concentrate on the if branch.

First, we will open the `try` block and call the `navigator.share(shareData)` inside. Now the native dialog will open up with all possible share targets. With `await` we will wait until the promise is fulfilled. That means, until the user cancel the sharing or chose a share target. On cancel, the code above will run the `catch` block. If the user finally shares the data via a share target, then the promise will be resolved. Then we successfully shared some data with the Web Share API 🎉

ℹ️ **Important note**: Even if the sharing was successful, we didn't get any detailed information. The promise itself will just return `undefined`. There is no way to get the chosen share target of the user. I know that's a bit disappointing, but there is a good reason.

> There is a requirement to not allow the website to learn which apps are installed, or which app was chosen from `share()`, because this information could be used for fingerprinting, as well as leaking details about the user's device. *https://www.w3.org/TR/web-share/*

### Files member
Now it's time to talk about the `files` member. This one contains all shared files as an array. By adding this member to you `shareData`, we have to handle binary data and the code gets a little more complex. But don't be afraid, we will go through it together.

So time for some imagination 💭 again...
Imagine you want to share a super cool looking image, if the user clicks on our share button. We need to load the file like this:

```javascript
const image = await fetch('./super-cool-looking.jpeg');
const blob = await image.blob();
const file = new File([blob], 'image.jpg', { type: 'image/jpeg' });
const filesArray = [file];

// add it to the shareData
shareData.files = filesArray;
```

In these lines of code we fetch the image, convert the raw data to BLOB (binary large object) and create a new File with the File interface. Then we just put it into an array. Of course, it's possible for you to add their more than one file.
In the end, we just add the files property to the `shareData` object and initialize it with the `filesArray`.

Now you can share images via the Web Share API, but be aware that this feature is not supported in all browsers. There is also an opportunity to check, if file sharing is possible in the browser. You can use the `navigator.canShare()` function for this. It could look like this for our example:

```javascript
if(navigator.canShare && navigator.canShare({files: filesArray})) {
  await navigator.share(shareData);
} else {
  console.log('File sharing is not supported.');
}
```

The function has one parameter like the `share()` function and will return `true` or `false`, if the browser can share the object (`shareData`) you put in.
But there is a big disadvantage for this approach, because this functionality is experimental and is not supported everywhere. Sadly, file sharing isn't that easy to handle on every device and browser. But as always, do it progressive (like above) and support modern browsers.

---

## When to use?
One important note, the `navigator.share()` function will just work on a user interaction (e.g. click event handler). That means, you can not run the code on page load. Which is good, because otherwise we would have the next stupid thing like cookie banners or push notification permission request.

---

## Which Browsers support the Web Share API?
I don't want to talk about it in detail, because it could be outdated. So here is the link to [Web Share API support](https://caniuse.com/web-share).

---

## Conclusion
As shown above, the modern way of sharing data in JS makes the code quiet easy and understandable. Also it's easy to maintain the code and we get the native share mechanism, which will also support future social platforms automatically. I'm a real fan of this approach and would recommend to give it a try in your next project. If you already made some experiences with this API, please share your thoughts in the comment section below. 👇

---

My sources
* [w3c Web Share](https://www.w3.org/TR/web-share/)
* [mdn Web Share API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share)
* [web.dev Web Share](https://web.dev/web-share/)