react-native-webview: navigator.share (Web Share API) is not working inside a Webview

Bug description:

I have a <WebView> in my app built using Expo and React Native.

The webview opens a page that uses the Web Share API, i.e. navigator.share({ url: 'url' }).

However, navigator.share is not available inside the webview.

If the same page is opened within a normal browser, sharing works as expected.

To Reproduce:

Call navigator.share from inside a page opened in a webview and see that it’s undefined.

Expected behavior:

navigator.share should be defined and working.

Environment:

  • OS: Android
  • OS version: 8
  • react-native version: "react-native": "https://github.com/expo/react-native/archive/sdk-35.0.0.tar.gz",
  • react-native-webview version:

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 19

Most upvoted comments

Solution by overriding navigator.share() to forward param to react native side for handling:

  1. Inject js before content loaded
// pass this in WebView injectedJavaScriptBeforeContentLoaded props
const injectedJavaScriptBeforeContentLoaded = `
if (navigator.share == null) {
  navigator.share = (param) => {
     window.ReactNativeWebView.postMessage('share:' + JSON.stringify(param));
  };
};
true;
`;
  1. Add onMessage callback to handle passed params, call react native’s Share to invoke native share sheet
import { Share } from "react-native"

interface WebShareAPIParam {
  // ref https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share
  url?: string;
  text?: string;
  title?: string;
  // files unhandled
}

// pass this in WebView onMessage props
const onMessage = useCallback(async (e: WebViewMessageEvent) => {
  const {data} = e.nativeEvent;
  if (data.startsWith('share:')) {
    try {
      const param: WebShareAPIParam = JSON.parse(data.slice('share:'.length));
      if (param.url == null && param.text == null) {
        return;
      }
      await Share.share(
        {
          title: param.title,
          message: [param.text, param.url].filter(Boolean).join(' '), // join text and url if both exists
          url: param.url,
        },
        {
          dialogTitle: param.title,
          subject: param.title,
        },
      );
    } catch (e: unknown) {
      console.error(e);
    }
  }
}, []);

Still an issue in 2022. Did anyone find a solution not involving Java or Koplin?

This is still a problem in late 2021. The navigator.share() method has been around since 2017, but it’s still not supported in the Android WebView. This is not specific to React Native, so I don’t know if there is any solution that can come from this project apart from instructions on how to do it directly from java / kotlin.

Luckily, it’s easy enough to launch a share intent from native android code. All that is needed is an interface object to expose methods that can be called from javascript. @alpkahveci linked to some code that shows how to add such an interface in java.

Here’s an example on how to do it using Kotlin:


  1. In your android project, just call addJavascriptInterface in your WebView to attach an interface to it.

MainActivity.kt

package com.your.app

class MainActivity : AppCompatActivity() {
  private lateinit var webViewComponent: WebView

  override fun onCreate(savedInstanceState: Bundle?) {
    setupWebview()
  }

  private fun setupWebview() {
    webview.settings.javaScriptEnabled = true
    // ... any other webview settings here

    val webAppInterface = WebAppInterface(this) // <- methods in this interface will become available to Javascript
    webview.addJavascriptInterface(webAppInterface, "Android") // <- methods will be exposed in global obj named Android
  }
}
  1. Then create the interface class that will expose any method you want to javascript. In this case we’ll expose one that launches an activity for a simple share intent.

WebAppInterface.kt

package com.your.app

import androidx.core.content.ContextCompat.startActivity // <- helper to start an activity from any class

class WebAppInterface(private val mContext: Context) {
  @JavascriptInterface // <- this decorator is what exposes the method
  fun nativeShare(title: String, text: String, url: String) {
    val sendIntent: Intent = Intent().apply {
      action = Intent.ACTION_SEND
      putExtra(Intent.EXTRA_TITLE, title)
      putExtra(Intent.EXTRA_TEXT, "$text $url")
      type = "text/plain"
    }
    val shareIntent = Intent.createChooser(sendIntent, null)
    startActivity(mContext, shareIntent, null)
  }
}
  1. And then in Javascript.

Component.jsx

const Component = () => {
  const shareData = {
    title: 'Title to appear in the share sheet title bar',
    text: `Some text to be shared`,
    url: 'https://anyurl.com'
  }
  const handleShare = async () => {
    if ('Android' in window) {
      Android.nativeShare(shareData.title, shareData.text, shareData.url)
      return
    }
    try {
      await navigator.share(shareData)
      console.log('shared successfully')
    } catch (err) {
      console.log('Error: ' + err)
    }
  }
  return (
    <button onClick={handleShare}>Share</button>
  )
}

export default Component

It’s still driving me crazy 30-10-2020

navigator.share is not supported in Webview: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share

Guyz! I have a good news. You can do it with native and javascript. here is the solution. I hope it will work for you 😃

How do you re-open this issue? It is still a problem…

Hello 👋, this issue has been opened for more than 2 months with no activity on it. If the issue is still here, please keep in mind that we need community support and help to fix it! Just comment something like still searching for solutions and if you found one, please open a pull request! You have 7 days until this gets closed automatically

@denissb

@Simoni How are you getting a reference to webview object in MainActivity? This is the only part I can’t quite seem to figure out.

It’s the ID of the webview component in the activity XML. In Kotlin these UI bindings are exposed automatically, but it’s the equivalent of findViewById.

If you created your Kotlin project from Android Studio this should just work. But if not, you can check this out: https://stackoverflow.com/questions/46882421/cant-access-edittext-or-other-ui-components-with-kotlin

Does anyone have a solution?

Any updates on this?

omg why does not work in android webview? It is not normal. i am opening in mobile browser, it is working perfectly but in webview app not work! (they have completely same user agent !!)

I am facing the same issue. If in iOS working then why can’t we can run in Android webview?