Click event handler not working when embedded in template string for React Native WebView

I’m building a React Native app and having trouble with click events in injected JavaScript code. I’m using react-native-webview to load a feedback form. When users submit the form, a “Thank you” message appears and I want the app to automatically navigate back after 3 seconds.

I’m trying to use the injectedJavaScript prop to add custom JavaScript. The timeout function works fine when called directly, but when I try to attach it to a button’s click event using querySelector, nothing happens. Here’s my code:

import React from "react";
import { View, StyleSheet, ActivityIndicator } from "react-native";
import { WebView } from "react-native-webview";
import { useNavigation } from "@react-navigation/native";

const formUrl = "https://example-form.com/";

export default function FeedbackScreen() {
  const nav = useNavigation();
  const customJS = `
    document.querySelector('#main-container').style.marginTop = 25;
    document.getElementsByTagName('h2')[0].style.visibility = 'hidden';
    document.getElementsByTagName('logo')[0].style.visibility = 'hidden';
    ${setTimeout(() => { // this part works fine
      nav.goBack();
    }, 3000)}
    document.querySelector('.submit-btn').onclick= ${function handleClick() { // this doesn't work
      setTimeout(() => {
        nav.goBack();
      }, 3000);
    }};
  `;

  return (
    <View style={styles.wrapper}>
      <WebView
        source={{ uri: formUrl }}
        startInLoadingState={true}
        renderLoading={() => <ActivityIndicator />}
        onMessage={() => {}}
        injectedJavaScript={customJS}
        javaScriptEnabled={true}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  wrapper: {
    flex: 1,
    justifyContent: "center",
    marginTop: 150,
    backgroundColor: "#f5f5f5",
    padding: 10,
  },
});

Why isn’t the onclick event firing? Is there a different way to handle click events in React Native WebView injected JavaScript?

you’re stringifying functions wrong in that template literal. ${function...} doesn’t work - it just becomes [object Object]. plus nav.goBack() won’t exist in the webview anyway. write your functions as plain strings inside customJS and use postMessage to talk back to the React Native side.

You’re mixing JavaScript string interpolation with template literals wrong. When you use ${setTimeout(...)} and ${function handleClick() {...}}, you’re trying to interpolate the execution results instead of the function code as strings.

Hit this exact problem last year building a similar webview integration. Define your functions as proper string literals within the injected JavaScript. Restructure your customJS like this:

const customJS = `
  document.querySelector('#main-container').style.marginTop = 25;
  document.getElementsByTagName('h2')[0].style.visibility = 'hidden';
  document.getElementsByTagName('logo')[0].style.visibility = 'hidden';
  
  function handleSubmit() {
    setTimeout(function() {
      window.ReactNativeWebView.postMessage('navigate_back');
    }, 3000);
  }
  
  var submitBtn = document.querySelector('.submit-btn');
  if (submitBtn) {
    submitBtn.onclick = handleSubmit;
  }
`;

I’m using window.ReactNativeWebView.postMessage() instead of directly calling nav.goBack() because the injected JavaScript runs in the webview context and doesn’t have access to your React Native navigation object. You’ll need to handle the message in your onMessage prop to trigger the actual navigation.

Your JavaScript is broken because of bad string concatenation. You’re trying to use React Native functions inside the webview, but they don’t exist there. I hit this exact problem with a customer survey project.

Webviews run their own JavaScript environment and can’t access your React Native navigation directly. You need to stringify functions properly and use the message bridge.

Try this:

const customJS = `
  document.querySelector('#main-container').style.marginTop = '25px';
  document.getElementsByTagName('h2')[0].style.visibility = 'hidden';
  
  // Wait for DOM to be ready
  setTimeout(function() {
    var submitButton = document.querySelector('.submit-btn');
    if (submitButton) {
      submitButton.addEventListener('click', function() {
        setTimeout(function() {
          window.ReactNativeWebView.postMessage('goBack');
        }, 3000);
      });
    }
  }, 1000);
`;

Then handle the message in your WebView:

onMessage={(event) => {
  if (event.nativeEvent.data === 'goBack') {
    nav.goBack();
  }
}}

Use addEventListener instead of onclick, and communicate through the message bridge rather than calling React Native functions from inside the webview.