React

Build browser extension UIs with React using the default Extension.js pipeline.

Extension.js detects React from your project dependencies and configures JSX/TSX transforms, React aliases, and development refresh integration automatically.

When React is a good fit

  • You are building rich UI surfaces like popup, options, sidebar, or new tab.
  • You want component reuse across extension pages and content-script UI mounts.
  • You already use React in web projects and want the same development model in extensions.

Template examples

new-react

new-react template screenshot

Build a React new-tab experience with a ready-to-run project structure.

npm
pnpm
yarn
npx extension@latest create my-extension --template=new-react

Repository: extension-js/examples/new-react

content-react

content-react template screenshot

Inject React UI into existing pages with a content-script-first setup.

npm
pnpm
yarn
npx extension@latest create my-extension --template=content-react

Repository: extension-js/examples/content-react

Usage with an existing extension

If you want to add React to an existing extension:

Installation

Install React runtime dependencies:

npm
yarn
pnpm
bun
npm install react react-dom

If you use TypeScript, also install React type packages:

npm
yarn
pnpm
bun
npm install -D @types/react @types/react-dom

Configuration

Common React file patterns:

  • JavaScript: *.jsx
  • TypeScript: *.tsx

Development behavior

In development mode, Extension.js enables React refresh integration when React is detected.

  • react-refresh and @rspack/plugin-react-refresh are used for refresh behavior.
  • If optional refresh dependencies are missing, Extension.js installs them and asks for a restart.
  • React aliases are applied to help keep a single React/runtime instance in your bundle.

Usage examples

In a new tab extension

Example page entry:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>New Extension</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this extension.</noscript>
    <div id="root"></div>
  </body>
  <script src="./index.tsx"></script>
</html>
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);
export default function App() {
  return <h1>Hello, Extension!</h1>;
}

In a content_script file

For content scripts, render React into an injected root element:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./content.css";

const rootDiv = document.createElement("div");
rootDiv.id = "extension-root";
document.body.appendChild(rootDiv);

const root = ReactDOM.createRoot(rootDiv);
root.render(<App />);

Next steps

Video walkthrough

Video demo soon: react extension workflow