Skip to content

Repository files navigation

Open IG - An Instagram alternative to avoid doomscrolling

image

How it works

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.

What the app does

  • Renders a fullscreen WebView pointing to Instagram.
  • Keeps cookies and DOM storage to preserve the session.
  • Opens external navigation outside the WebView.
  • Handles the Android back button using the WebView history.
  • Injects an early script with mods to alter the Instagram experience.

Structure

App.tsx
src/
  features/
    instagram/
      InstagramWebView.tsx
  webview/
    injectedJavaScript.ts
    main.ts
    modules/
      appInstallBannerSuppressor.ts
      reelsButtonRemover.ts
      suggestedReelsBlocker.ts

General flow

1. App entrypoint

App.tsx only mounts InstagramWebView.tsx.

2. Native container

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

3. Injected task clip

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

4. Shared runtime

main.ts generates the final script that:

  • creates window.__OPEN_IG__
  • exposes postMessage
  • instantiates registered modules
  • automatically runs the modules marked with autoStart

5. Mods

Each file inside src/webview/modules represents an independent modification.

Current mods:

  • appInstallBannerSuppressor: writes ig_uta_b into localStorage before 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 /reels or /reels/.

How to add a new mod

1. Create the module file

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 optionally stop().
  • If the mod touches the DOM frequently, prefer MutationObserver with specific filters and batching through requestAnimationFrame.
  • Avoid full-document scans on every mutation.

2. Register the mod

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 inside window.__OPEN_IG__.modules
  • factoryName: exact name of the function created inside the module string
  • script: string generated by the module
  • autoStart: if true, the runtime automatically executes start()

Conventions

  • Keep each mod isolated in its own file.
  • Do not mix multiple product changes into the same module.
  • Keep injectedJavaScript.ts as 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.

Development

Install dependencies:

npm install

Start the app:

npx expo start

Recommended validation when editing mods

To 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/*.ts
npx eslint src/webview/injectedJavaScript.ts src/webview/main.ts src/webview/modules/*.ts

Build

If tou need to build and deploy the app, follow the instructions in the following files:

  • Android: ./docs/android.md
  • iOS: [pending]

Important note

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.

About

Instagram without doomscrolling

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages