Juji Blog

Adding Share Button To a React Site

#nextJs#react#share

How do you add share functionality in your React site? How do you make it compatible with Web Share API? That is the question this post is trying to answer.

Sharing Sites and Web Share API

We already know this. Sites are shareable. We can share them on social media like Facebook, Twitter, Pinterest, or other. But not everything is easy, because some browser do not (at the writing of this article) implement the Web Share API. Checkout the image below for compatibility table:

Browser compatibility table from MDN on 22-04-2023

Firefox on Desktop, currently, does not support the Web Share API. Maybe it's on development, since they say it's accessible if we turn it on via the dom.webshare.enabled preference. However, i did that, and nothing shows. Probably because i'm using a Mac. Also, Chrome and Opera on Mac does not support it (yet?).

So, we need to add extra stuff to enable the sharing functionality for some browser. OK, that's doable. First, let's do the standard way of doing things. After that, we can add some more things to enable sharing on the non-supporting browsers.

Checking The Web Share API Compatibility

First, let check how the Web Share API looks like across browser. Actually, I can only check two browsers: Microsoft Edge and Safari. Since i'm on a Mac and those two are the ones currently enabling the Web Share API.

We are using the Web Share Test tool from w3c. On Microsoft Edge, It looks like this:

Web Share on Microsoft Edge

Looks okay to me. We can just use it straight forward. How about Safari? Let's check:

Web Share on Safari

Oh no, I don't think that's good enough. Ok then, we will treat safari as a non-supporting browser. We will prevent Web Share API from being used on Safari Desktop.

So, we need to check the client support for Web Share API, while preventing it in Safari Dekstop. Let's create a function for that.

1'use client'
2
3import UAParser from 'ua-parser-js'
4
5function isDektopSafari(){
6
7 const parserResult = (new UAParser()).getResult()
8 return parserResult.browser.name === 'Safari' &&
9 parserResult.device.model === "Macintosh"
10
11}
12
13export default function isWebShareCompatible(data: {
14 title: string,
15 text: string,
16 url: string
17}){
18
19 return !isDektopSafari() &&
20 navigator?.canShare &&
21 navigator?.canShare(data)
22
23}

Why am i not creating the interface for File Object? It is not important in this example. It is out of scope.

I'm currently working with NextJS 14.2.2. Since we are using NextJs, the use client directive is necessary. Because the User Agent string and the navigator Window property resides on the client.

Creating The Share Button

Now, let's add a button to our React Component. This will trigger the Web Share function when clicked:

1import isWebShareCompatible from './isWebShareCompatible'
2
3export default function Share(){
4
5 function onClick(){
6
7 const shareData = {
8 title: document.title,
9 url: window.location.href,
10 text: document
11 .querySelector("meta[name='description']")
12 ?.getAttribute("content") || '',
13 }
14
15 if(isWebShareCompatible(shareData)){
16 navigator.share(data)
17 }else{
18
19 // what to do?
20 alert('not compatible')
21
22 }
23
24 }
25
26 return <button onClick={onClick}>Share</button>
27
28}

Okay, that's the basic of it. Next, we will need to add support for other browsers.

Adding Support For The Non-Supporting

Haha.. the title... Okay so we need to support other browsers. How do we do that..? huh huh...? We can create our own pop-out for this. It's an easy task.

Ok! Let's found a library to do this. What do i need to think about on selecting a social sharing library..?

Well, it's 2024. That means Facebook, X (Twitter), Threads, LinkedIn, and Reddit Should be available. At least, that's what i can think of.

Upon searching on the web, i found dv-social-share. Other similar packages like react-share or react-share-social doesn't seem to support Threads. I wonder what people are up to..?

Using dv-social-share means i have to customize the styling. Because i just don't agree with the default styling. O well, that's just part of being a web developer.

Ok, so here's the final code:




1'use client'
2
3import isWebShareCompatible from './isWebShareCompatible'
4import SocialButtons from './social-buttons'
5import { useState } from 'react'
6
7type SocialData = {
8 title: string
9 url: string
10 text?: string
11}
12
13export default function Share(){
14
15 const [
16 open,
17 setOpen
18 ] = useState<SocialData|null>(null)
19
20 function onClose(){
21 setOpen(null)
22 }
23
24 function onClick(){
25
26 const shareData = {
27 title: document.title,
28 url: window.location.href,
29 text: document
30 .querySelector("meta[name='description']")
31 ?.getAttribute("content") || '',
32 }
33
34 if(isWebShareCompatible(shareData)){
35 navigator.share(shareData)
36 }else{
37 setOpen(shareData)
38 }
39
40 }
41
42 return <>
43 <button onClick={onClick}>Share</button>
44 { open ?
45 <SocialButtons
46 title={open.title}
47 url={open.url}
48 onClose={onClose} /> : null }
49 </>
50
51}

Checkout the example:

Or, you know, click on the button(s) bellow.