Web Share Web API

Authors
  • avatar
    Name
    Austin Howard
Updated on
web sharing example

If sharing text, urls, or files is an important part of your web application, your goal should be to make sharing as convenient as possible for your users. A great tool for this is the Web Share API. The Web Share API allows you to trigger the native sharing capabilities of the device. This means that you can use it share to other apps on the device, such as email, messaging, and social media apps.

Example

Bonk

Here is a simple example of how you can use the Web Share API to share text, urls and files.

The useWebShare hook below allows passing in an object with the following properties:

  • title - The title of the shared content.
  • text - The text of the shared content.
  • url - The URL of the shared content.
  • files - An array of string URLs to files that should be shared.

Some environments and devices may not support sharing files. In this case, the files property will be ignored, and only the title, text, and url properties will be shared.

// use-webshare.tsx
export type PreFilesShareData = {
  title?: string
  text?: string
  url?: string
  files?: string[]
}

export type WebShareData = {
  title?: string
  text?: string
  url?: string
  files?: File[]
}

export const useWebShare = () => {
  const share = async (data: PreFilesShareData) => {
    if (!navigator.share) {
      console.error('Web Share API not supported.')
      return null
    }

    if (!data.files?.length) {
      try {
        await navigator.share({ title: data.title, text: data.text, url: data.url })
      } catch (error) {
        console.error('Error sharing:', error)
      }
      return
    }

    let dataWithFiles: WebShareData = {
      ...data,
      files: [],
    }

    const fetchErrors = []

    if (data?.files) {
      for (const url of data.files) {
        try {
          const fullUrl = new URL(url, window.location.origin)
          const fileName = fullUrl.pathname.split('/').pop()
          const response = await fetch(fullUrl)
          const blob = await response.blob()
          dataWithFiles.files.push(new File([blob], fileName, { type: blob.type }))
        } catch (error) {
          fetchErrors.push({ url, error })
        }
      }

      if (fetchErrors.length > 0) {
        console.warn('Some files could not be fetched:', fetchErrors)
      }

      if (!navigator.canShare({ files: dataWithFiles.files })) {
        console.error('The browser does not support sharing these files.', dataWithFiles.files)
        const fallbackUrl = new URL(data.files[0], window.location.origin).href
        dataWithFiles.url = fallbackUrl
        delete dataWithFiles.files
      }

      try {
        await navigator.share(dataWithFiles)
      } catch (error) {
        console.error('Error sharing:', error)
      }
    }
  }

  return { share }
}

Here is how you can use the useWebShare hook in a React component:

// implementation
import { useWebShare, PreFilesShareData } from '@/lib/sharing/use-webshare'
import { ReactNode } from 'react'

export type Props = {
  children: ReactNode
} & PreFilesShareData

export const ShareButton = ({ title = 'Default title', text, url, files, children }: Props) => {
  const { share } = useWebShare()

  return <button onClick={() => share({ title, text, url, files })}>{children}</button>
}

Keep in mind that the user must trigger the share action. You cannot trigger the share action programmatically. See transient activation for more information.