Skip to content

Instantly share code, notes, and snippets.

@smontlouis
Created December 24, 2025 12:30
Show Gist options
  • Select an option

  • Save smontlouis/0ac5998239a4a6a7b8909c1f3ee36ee2 to your computer and use it in GitHub Desktop.

Select an option

Save smontlouis/0ac5998239a4a6a7b8909c1f3ee36ee2 to your computer and use it in GitHub Desktop.
expo 54 android webview fix
diff --git a/src/async-require/setupHMR.ts b/src/async-require/setupHMR.ts
index 1278b6eb12c4702d2c2487d7780266c454e5116e..d8ef51578019b46e9ce84b7ce0ccd145005dc065 100644
--- a/src/async-require/setupHMR.ts
+++ b/src/async-require/setupHMR.ts
@@ -3,7 +3,7 @@ import HMRClient from './hmr';
if (
typeof window !== 'undefined' &&
// @ts-expect-error: Added via react-native-webview
- typeof window.$$EXPO_INITIAL_PROPS !== 'undefined'
+ typeof window.ReactNativeWebView !== 'undefined'
) {
// Sets up developer tools for web platforms when running in a webview. This ensures that logs are visible in the terminal.
// We assume full control over the console and send JavaScript logs to Metro.
diff --git a/src/dom/dom-entry.tsx b/src/dom/dom-entry.tsx
index f908ff5afb9706976038f2f9c4cf449dfa6cffd0..9b98cf9c38cddbf827a3e9b67081243835e1eae3 100644
--- a/src/dom/dom-entry.tsx
+++ b/src/dom/dom-entry.tsx
@@ -68,11 +68,6 @@ export function registerDOMComponent(AppModule: any) {
function DOMComponentRoot(props: Record<string, unknown>) {
// Props listeners
const [marshalledProps, setProps] = React.useState(() => {
- if (typeof window.$$EXPO_INITIAL_PROPS === 'undefined') {
- throw new Error(
- 'Initial props are not defined. This is a bug in the DOM Component runtime.'
- );
- }
return window.$$EXPO_INITIAL_PROPS;
});
@@ -88,7 +83,7 @@ export function registerDOMComponent(AppModule: any) {
}, [setProps]);
const proxyActions = React.useMemo(() => {
- if (!marshalledProps.names) return {};
+ if (!marshalledProps?.names) return {};
// Create a named map { [name: string]: ProxyFunction }
// TODO(@kitten): Unclear how this is typed or shaped
return Object.fromEntries(
@@ -96,7 +91,11 @@ export function registerDOMComponent(AppModule: any) {
return [key, ACTIONS[key]];
})
);
- }, [marshalledProps.names]);
+ }, [marshalledProps?.names]);
+
+ if (!marshalledProps) {
+ return null;
+ }
return <AppModule {...props} {...(marshalledProps.props || {})} {...proxyActions} />;
}
diff --git a/src/dom/dom-hooks.ts b/src/dom/dom-hooks.ts
index 1e29db942615b8d5b00afb94733e5c599b731d3f..4ca022d1360f1c33ba3393820b6f436c2c56291a 100644
--- a/src/dom/dom-hooks.ts
+++ b/src/dom/dom-hooks.ts
@@ -18,9 +18,7 @@ export function useDOMImperativeHandle<T extends DOMImperativeFactory>(
) {
const isTargetWeb =
// @ts-expect-error: Added via react-native-webview
- typeof window.ReactNativeWebView === 'undefined' &&
- // @ts-expect-error: Added via expo/dom
- typeof window.$$EXPO_INITIAL_PROPS === 'undefined';
+ typeof window.ReactNativeWebView === 'undefined';
const stubHandlerFactory = useCallback(() => ({}) as T, deps ?? []);
diff --git a/src/dom/dom.web.ts b/src/dom/dom.web.ts
index f582c496e859a05cc6c767e939f73ef42224fbd2..d269d1ae1768bf7d5667c70131e692e9335afaf7 100644
--- a/src/dom/dom.web.ts
+++ b/src/dom/dom.web.ts
@@ -2,5 +2,6 @@ export * from './dom-hooks';
// TODO: Maybe this could be a bundler global instead.
export const IS_DOM =
+ typeof window !== 'undefined' &&
// @ts-expect-error: Added via react-native-webview
- typeof $$EXPO_INITIAL_PROPS !== 'undefined';
+ typeof window.ReactNativeWebView !== 'undefined';
diff --git a/src/dom/marshal.tsx b/src/dom/marshal.tsx
index 26e42d6da28d5060ce6564377c7851d6850196b7..d19126e2c3ad9b9d35828fbc4a155339500d297a 100644
--- a/src/dom/marshal.tsx
+++ b/src/dom/marshal.tsx
@@ -3,8 +3,6 @@ import { DOM_EVENT, NATIVE_ACTION, NATIVE_ACTION_RESULT } from './injection';
const IS_DOM =
typeof window !== 'undefined' &&
- // @ts-expect-error: Added via expo/dom
- typeof window.$$EXPO_INITIAL_PROPS !== 'undefined' &&
// @ts-expect-error: Added via react-native-webview
typeof window.ReactNativeWebView !== 'undefined';
diff --git a/src/dom/webview-wrapper.tsx b/src/dom/webview-wrapper.tsx
index 93041ac3cb77ebce5d3712a00269224cac494e57..d1a0c560c5376e1d815bfff985fe5f45b34705de 100644
--- a/src/dom/webview-wrapper.tsx
+++ b/src/dom/webview-wrapper.tsx
@@ -132,15 +132,24 @@ const RawWebView = React.forwardRef<object, Props>((props, ref) => {
...dom,
containerStyle: [containerStyle, debugZeroHeightStyle, dom?.containerStyle],
onLayout: __DEV__ ? debugOnLayout : dom?.onLayout,
+ onLoad: () => {
+ emit({ type: '$$props', data: smartActions });
+ },
injectedJavaScriptBeforeContentLoaded: [
// On first mount, inject `$$EXPO_INITIAL_PROPS` with the initial props.
`window.$$EXPO_INITIAL_PROPS = ${JSON.stringify(smartActions)};true;`,
- dom?.matchContents ? getInjectBodySizeObserverScript() : null,
dom?.injectedJavaScriptBeforeContentLoaded,
'true;',
]
.filter(Boolean)
.join('\n'),
+ injectedJavaScript: [
+ dom?.matchContents ? getInjectBodySizeObserverScript() : null,
+ dom?.injectedJavaScript,
+ 'true;',
+ ]
+ .filter(Boolean)
+ .join('\n'),
// @ts-expect-error: TODO(@kitten): untyped ref for now
ref: webviewRef,
source,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment