react-native-webview: mailto: and tel: links do not work in webview

Bug description:

If the page opened in the webview has any mailto: or tel: links, pressing them causes the webview to return the generic android page and fire the error events.

I believe the error is: net::ERR_UNKNOWN_URL_SCHEME (android) and unsupported URL (iOS)

Happens on both iOS and Android

To Reproduce:

Load any page with mailto and/or tel links.

Expected behavior:

Other schemas should be working

Screenshots/Videos:

Environment:

  • OS: Android and iOS
  • OS version: 8.1, 10, 13.2.3
  • react-native version: 0.61.5
  • react-native-webview version: 7.6.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 9
  • Comments: 31 (2 by maintainers)

Commits related to this issue

Most upvoted comments

@Visakeswaran not really, I just handled the links case by case with the onShouldStartLoadWithRequest prop and something like this:

function onShouldStartLoadWithRequest(request){

  // short circuit these
  if (!request.url ||
    request.url.startsWith('http') ||
    request.url.startsWith("/") ||
    request.url.startsWith("#") ||
    request.url.startsWith("javascript") ||
    request.url.startsWith("about:blank")
  ) {
    return true;
  }

  // blocked blobs
  if(request.url.startsWith("blob")){
    Alert.alert("Link cannot be opened.");
    return false;
  }

  // list of schemas we will allow the webview
  // to open natively
  if(request.url.startsWith("tel:") ||
    request.url.startsWith("mailto:") ||
    request.url.startsWith("maps:") ||
    request.url.startsWith("geo:") ||
    request.url.startsWith("sms:")
    ){

    Linking.openURL(request.url).catch(er => {
      Alert.alert("Failed to open Link: " + er.message);
    });
    return false;
  }

  // let everything else to the webview
  return true;
}

Does this still work? We found that when we open an intent:// link, onShouldStartLoadWithRequest prop is not called anymore (An error Can't open url: intent://xxx is displayed instead)

It used to work on 5.x.x 😢

Even when I added “intent://*” to whitelist, it still cannot open url intent://xxxx. Is there anyone has solution for this issue?

replace originWhitelist={[*]} with originWhitelist={['http://*', 'https://*', 'intent://*']}

add originWhitelist={['https://*', 'http://*', 'tel:*', 'mailto:*']} works for me.

Update:

We found that we need to add originWhitelist={['http://*', 'https://*', 'intent://*']} so that intent:// link can be handled with onShouldStartLoadWithRequest (Note that mailto/tel/geo/etc. would likely have the same problem)

The commit with breaking changes: https://github.com/react-native-community/react-native-webview/commit/0442126

I fixed it by adding

originWhitelist={[
                    "http://*",
                    "https://*",
                    "intent://*",
                    "tel:*",
                    "mailto:*"
                ]}

and

onError={event => {
                    webViewRef.current?.goBack();
                }}
                onNavigationStateChange={event => {
                    if (
                        event.url !==
                        "your original source uri here"
                    ) {
                        webViewRef.current?.stopLoading();
                        Linking.openURL(event.url);
                    }
                }}

@Visakeswaran not really, I just handled the links case by case with the onShouldStartLoadWithRequest prop and something like this:

function onShouldStartLoadWithRequest(request){

  // short circuit these
  if (!request.url ||
    request.url.startsWith('http') ||
    request.url.startsWith("/") ||
    request.url.startsWith("#") ||
    request.url.startsWith("javascript") ||
    request.url.startsWith("about:blank")
  ) {
    return true;
  }

  // blocked blobs
  if(request.url.startsWith("blob")){
    Alert.alert("Link cannot be opened.");
    return false;
  }

  // list of schemas we will allow the webview
  // to open natively
  if(request.url.startsWith("tel:") ||
    request.url.startsWith("mailto:") ||
    request.url.startsWith("maps:") ||
    request.url.startsWith("geo:") ||
    request.url.startsWith("sms:")
    ){

    Linking.openURL(request.url).catch(er => {
      Alert.alert("Failed to open Link: " + er.message);
    });
    return false;
  }

  // let everything else to the webview
  return true;
}

Please note that this won’t work on the iOS simulator, but will on the physical device.

@Visakeswaran not really, I just handled the links case by case with the onShouldStartLoadWithRequest prop and something like this:

function onShouldStartLoadWithRequest(request){

  // short circuit these
  if (!request.url ||
    request.url.startsWith('http') ||
    request.url.startsWith("/") ||
    request.url.startsWith("#") ||
    request.url.startsWith("javascript") ||
    request.url.startsWith("about:blank")
  ) {
    return true;
  }

  // blocked blobs
  if(request.url.startsWith("blob")){
    Alert.alert("Link cannot be opened.");
    return false;
  }

  // list of schemas we will allow the webview
  // to open natively
  if(request.url.startsWith("tel:") ||
    request.url.startsWith("mailto:") ||
    request.url.startsWith("maps:") ||
    request.url.startsWith("geo:") ||
    request.url.startsWith("sms:")
    ){

    Linking.openURL(request.url).catch(er => {
      Alert.alert("Failed to open Link: " + er.message);
    });
    return false;
  }

  // let everything else to the webview
  return true;
}

This still crashed webview for me.

But, luckily this commit released with 11.0.0 features a new option setSupportMultipleWindows, which when set to true opens links that open in new tabs/windows (such as <a target="_blank">) will now prompt to open in the system browser, rather than re-using the current WebView.

<WebView
	// links that open in new tabs/windows will now prompt to open in the system browser
    setSupportMultipleWindows={true}
	// other props here
  />

So, once enabled the flag(default set to true), make the link open in a new tab

<a target="_blank" rel="nofollow noreferrer noopener" href="tel:+91xxxxxxxxx">Call now</a>

Update: if changing target is not a viable option, I suggest adding some fallbacks when error is thrown

const webViewRef = React.useRef(null);

<WebView
  ref={webViewRef}
  setSupportMultipleWindows={true}
  onError={({nativeEvent}) => {
              // Function that is invoked when the WebView load fails.
              if (nativeEvent.url.startsWith('tel:') && nativeEvent.canGoBack) {
                // Fallback for tel links without target="_blank"
                webViewRef.current.goBack();
              }
           }}
/>