DEV Community

David Dal Busco
David Dal Busco

Posted on • Updated on

How to implement the Web Share API with a fallback


Photo by Noiseporn on Unsplash

In the past weeks I often had to implement the Web Share API in several applications. As it is not yet supported by all browsers and devices, I always had to implement the same fallback, respectively I always used the open source Web Component I developed called “Web Social Share” as a fallback. That’s why I’m sharing my solution with this new blog post, hoping that maybe someday it will help someone or even better, that maybe someone will ping me back with a better solution 😁

Getting started

The following implementation is pretty straight forward. We are going to implement a share method which detects if the Web Share API is supported by the browser or not. If supported it will, I guess you get it, use and open the Web Share API otherwise it will open the share fallback.


Safari (left) supports Web Share API, Chrome (desktop, right) doesn’t support it and fallback to the Web Component “Web Social Share”

Main function and detection

First, we are going to implement a main share function which checks which share method should be use.

<script language="JavaScript">
  openShare = async () => {
    if (navigator && navigator.share) {
      await shareNative();
    } else {
      await shareFallback();
    }
  };
</script>

Web Share API

We are now going to implement the above method called shareNative() which will use the Web Share API.

This API accepts three parameters currently: url , text and title . Only one of these parameters should at least be provided but I suggest you to use text and url . Using these two will for example allow you to add a nice message when a user would share an URL on Twitter.

The Web Share API is promised based and we are going to implement it like the following:

function shareNative() {
  return new Promise(async (resolve) => {
    const shareUrl =
          `${window.location.protocol}//${window.location.host}`;

    await navigator.share({
      text: 'How to implement the Web Share API and a fallback',
      url: shareUrl,
    });

    resolve();
  });
}

We are using window.location to build dynamically the url we are going to share. You could replace this piece of code with any valid URL as a string if you rather like.

Web Social Share as Fallback

The Web Component I developed need firstly to be consumed. You could install it through npm but in this tutorial we are just going to use it with the help of Unpkg. For that purpose, we are going to add the following scripts to our HTML page:

<script type="module"
    src="https://unpkg.com/web-social-share@latest/dist/websocialshare/websocialshare.esm.js"></script>
<script nomodule 
    src="https://unpkg.com/web-social-share@latest/dist/websocialshare/websocialshare.js"></script>

The component doesn’t ship any icons respectively we will have to provide our own. I designed the component that way because I thought that it makes more sense to not “force” the developer to use an icon which might not fits its design. Therefore, in this tutorial, we are going to use the Ionicons which we are going to consume using Unpkg too. Of course, if your application already contains its own set of icons, just skip this step.

<script src="https://unpkg.com/ionicons@latest/dist/ionicons.js"></script>

Declaration

In opposition to the Web Share API, this component can’t guess which methods are available on the device where it runs. That’s why it only exposes a couple of share methods. In this tutorial we are going too implement the options Email, Twitter and WhatsApp.

To declare the component we are going to add it to the page, specifying that per default it should not be displayed ( show="false" ). We are also going to specify the icons we want to use. Note that these are passed to the component using slot and could be inline styled.

<web-social-share show="false">
  <ion-icon name="logo-twitter" ariaLabel="Twitter" slot="twitter"
            style="color: #00aced;">
  </ion-icon>
  <ion-icon name="mail" slot="email" ariaLabel="Email" 
            style="color: #ff8ea3;">
  </ion-icon>
  <ion-icon name="logo-whatsapp" ariaLabel="WhatsApp"
            slot="whatsapp"
            style="color: #25D366;">
   </ion-icon>
</web-social-share>

If you never use slot before and want to know a bit more about them, I wrote another article “A practical introduction to styling a Shadow DOM and Slots” which tries to explain practically how these could be used and styled.

Implementation

Now that we have consumed and declared the Web Component, we just have to implement the final piece with the implementation of the above mentioned method called shareFallback().

This function will query the DOM for a reference on the component (using document.querySelector ), define the share options and finally will trigger the display of the share options by modifying its show attribute (the component listen for changes on this attribute to display or not its toolbar).

function shareFallback() {
  return new Promise(async (resolve) => {
    const webSocialShare =
          document.querySelector('web-social-share');

    if (!webSocialShare || !window) {
      return;
    }

    const shareUrl =
          `${window.location.protocol}//${window.location.host}`;

    const share = {
      displayNames: true,
      config: [{
        twitter: {
          socialShareUrl: shareUrl,
          socialSharePopupWidth: 300,
          socialSharePopupHeight: 400
        }
      },{
        email: {
          socialShareBody: shareUrl
        }
      }, {
        whatsapp: {
          socialShareUrl: shareUrl
        }
      }]
    };
    // The configuration, set the share options
    webSocialShare.share = share;
    // Show/open the share actions
    webSocialShare.show = true;

    resolve();
  });
}

That’s it, we have implemented the Web Share API and a fallback 🎉

Trying it out

To try out the implementation, we could just add for example a button which calls openShare() when its clicked.

<button onClick="openShare()">Share</button>

If everything went according plan, the result should looks like the following:


Again, Safari (left) supports Web Share API and Chrome (desktop) doesn’t and use the fallback

Cherry on the cake 🍒🎂

Like I said in my introduction, I implemented the above solution in several applications the past weeks, I notably implemented it in the website and starter kit of DeckDeckGo, our upcoming open source web editor for presentations. Therefore don’t hesitate to have a look at the code and to ping me if you have a better solution to share or maybe even have a PR to submit to improves it 😉

To infinity and beyond 🚀

David

Discussion (16)

Collapse
phkla1 profile image
phkla1

I came across this while looking for a solution for an Ionic PWA (Capacitor). Looks very good but the documentation for using npm install isn't clear to me. After npm install I had expected that I would need to add scripts to angular.json? Or ionic.config? Etc? But I can't see any guidance on this. I did a first pass through and I got the "web-social-share is not a known element" error, which is not surprising. Can you give some more guidance on how to install this for Ionic?

Collapse
daviddalbusco profile image
David Dal Busco Author

Sure! The Web Components is developed with Stencil so you should be able to install it according the doc

Respectively, after npm install:

  1. Include schemas: [CUSTOM_ELEMENTS_SCHEMA] in the module your are loading the component

  2. defineCustomElements() in the main.ts

Let me know if it works out.

P.S.: It would be possible to ease the integration with the Angular output bundle but, I don't have that particular need, that's why I don't provide it currently. Not against a PR of course 😉.

Collapse
phkla1 profile image
phkla1

Thanks David. It worked! Really appreciate the prompt response. In addition to the Stencil setup the "display:block" styling was very important (as you pointed out in the documentation), and in addition if one is only using a few ionicons then "size=large" is also important. If you'd like me to compile my observations so as to update the documentation for Ionic let me know - there are many users of other browsers (Samsung, Kai) in my part of the world and I suspect your solution may be in use longer than you think!

Thread Thread
daviddalbusco profile image
David Dal Busco Author

Sweet happy to hear it worked out!

Sure if you don't mind compiling your observations, never against.

Thanks for giving it a try and for the feedback 😀.

Thread Thread
phkla1 profile image
phkla1

Wasn't sure how best to send this so I'm just putting it here and you decide how best to use it.

SOME NOTES ON USING WEB-SOCIAL-SHARE IN IONIC VIA NPM

  1. web-social-share is built using Stencil (stenciljs.com/docs/angular). Therefore after npm install you need to: i) update the relevant module (e.g. home.module) with schemas : [CUSTOM_ELEMENTS_SCHEMA] ii) update main.ts with a "defineCustomElements" call.

Typically you may add these 2 lines:
import {defineCustomElements} from 'web-social-share/dist/loader';
defineCustomElements();

However, in practice, you probably already have a defineCustomElements e.g. if you're using pwa-elements. So you need to do something like:
import * as socialShare from 'web-social-share/dist/loader';
socialShare.defineCustomElements();

  1. As stated in the documents using the "display:block" attribute within the web-social-share element is important - don't skip that.

  2. If you're using ionicons you will probably need to add a "size=large" attribute.

  3. As hinted in the docs: you would want to include an "import {WebSocialShareInput} from 'web-social-share';" so that you can specify the type of your shareOptions object i.e. if you activate the element via a line like "webshare.share = share", then "share" should have been defined as type WebSocialShareInput.

Thread Thread
daviddalbusco profile image
David Dal Busco Author

Really nice @phkla1 👍

I think we should add "Angular" next to "Ionic". This hasn't to be done, or has to be done differently, with other frameworks but, beside that, coolio 🤙.

We can maybe provide these notes in the repo in a separate new markdown file, like IONIC.md and add a note in the README.md like "For Ionic find more information here" or something which points to this specific doc.

What do you think? If that works for you, can you even send a PR?

Thread Thread
phkla1 profile image
phkla1

Hi @daviddalbusco . Yes, good idea on PR. I'll do that shortly.

Thread Thread
daviddalbusco profile image
David Dal Busco Author

❤️

Collapse
voidjuneau profile image
Juneau Lim • Edited on

I recently tried to use API to React without knowing nothing about Promise or nor XMLHttpRequest. I faced a bunch of pending promise returns and a bit panicked. I definitely have to study this topic.

Collapse
daviddalbusco profile image
David Dal Busco Author

It took me a couple of practice to use promises and async/await concept but once used to it, I use promises almost everywhere now.

Never tried React though, there is always something new to learn for everyone ;)

Collapse
voidjuneau profile image
Juneau Lim

I also just started. But I feel like I have to study JS more in the first place. The fact that there are only more things to learn is both glad and fearful.

Collapse
itsjzt profile image
Saurabh Sharma

we (a few weeks back) use something similar to

  • web share if supports
  • else copy to cliboard if supports
  • tell the user to copy manually
Collapse
daviddalbusco profile image
David Dal Busco Author

good solution too!

what do you use for the copy to clipboard? it's maybe actually a good idea too to add it as a default option of my fallback web component

Collapse
itsjzt profile image
Saurabh Sharma • Edited on

we used execCommand and navigator API developer.mozilla.org/en-US/docs/M...

Thread Thread
daviddalbusco profile image
David Dal Busco Author

cool cool cool, thx 👍

Really like it, so I implemented that idea and published it in my component straight away 😃

websocialshare.com

Thread Thread
itsjzt profile image
Saurabh Sharma

oh, thats great 🍺