Mobile app built with Expo whose only visible content is an Instagram WebView.
The app does not recreate Instagram's UI. Instead, it loads https://www.instagram.com/ inside a WebView and then injects a task clip before the page finishes loading in order to modify behavior, DOM, and storage through JavaScript.
- Renders a fullscreen
WebViewpointing to Instagram. - Keeps cookies and DOM storage to preserve the session.
- Opens external navigation outside the
WebView. - Handles the Android back button using the
WebViewhistory. - Injects an early script with mods to alter the Instagram experience.
App.tsx
src/
features/
instagram/
InstagramWebView.tsx
webview/
injectedJavaScript.ts
main.ts
modules/
appInstallBannerSuppressor.ts
reelsButtonRemover.ts
suggestedReelsBlocker.ts
App.tsx only mounts InstagramWebView.tsx.
InstagramWebView.tsx is the place where the React Native layer lives:
- creates the
WebView - loads
https://www.instagram.com/ - uses
injectedJavaScriptBeforeContentLoaded - intercepts external navigation
- listens for messages sent from the injected script
- handles Android back behavior
injectedJavaScript.ts builds the task clip that gets injected into the page.
That file should not contain large product-specific logic. Its responsibility is to:
- declare which mods exist
- register each mod
- delegate to a shared assembler
main.ts generates the final script that:
- creates
window.__OPEN_IG__ - exposes
postMessage - instantiates registered modules
- automatically runs the modules marked with
autoStart
Each file inside src/webview/modules represents an independent modification.
Current mods:
appInstallBannerSuppressor: writesig_uta_bintolocalStoragebefore page load to avoid the banner recommending the native app.suggestedReelsBlocker: removes suggested Reels nodes when it detects[data-reel-type="suggested"].reelsButtonRemover: removes the third-level ancestor of anchors that point to/reelsor/reels/.
Add a new file inside src/webview/modules/.
Example:
export function buildMyNewModuleScript(): string {
return `
function createMyNewModule() {
function start() {
// mod logic
}
return {
start: start,
stop: function() {},
};
}
`;
}Practical rules:
- The module must return a script as a string.
- Inside that script there must be a global factory, for example
createMyNewModule. - That factory must return an object with
start()and optionallystop(). - If the mod touches the DOM frequently, prefer
MutationObserverwith specific filters and batching throughrequestAnimationFrame. - Avoid full-document scans on every mutation.
Import it in injectedJavaScript.ts and add it to the injectedModules array.
Example:
import { buildMyNewModuleScript } from './modules/myNewModule';
const injectedModules: InjectedModuleRegistration[] = [
{
key: 'myNewModule',
factoryName: 'createMyNewModule',
script: buildMyNewModuleScript(),
autoStart: true,
},
];Fields:
key: public name insidewindow.__OPEN_IG__.modulesfactoryName: exact name of the function created inside the module stringscript: string generated by the moduleautoStart: iftrue, the runtime automatically executesstart()
- Keep each mod isolated in its own file.
- Do not mix multiple product changes into the same module.
- Keep
injectedJavaScript.tsas a registry/assembler, not a place for feature-specific logic. - If a mod depends on storage or early setup, it should live in the task clip that runs before content is loaded.
- If a mod operates on dynamic DOM, use selective observation instead of polling.
Install dependencies:
npm installStart the app:
npx expo startTo validate only the injection layer:
npx tsc --noEmit --target esnext --module esnext --moduleResolution bundler --lib esnext,dom --skipLibCheck src/webview/injectedJavaScript.ts src/webview/main.ts src/webview/modules/*.tsnpx eslint src/webview/injectedJavaScript.ts src/webview/main.ts src/webview/modules/*.tsIf tou need to build and deploy the app, follow the instructions in the following files:
- Android: ./docs/android.md
- iOS: [pending]
This application is intentionally a WebView container. Most product customization does not live in native components, but in the injected task clip and its mods.