Skip to main content
Extension.js detects React from your project dependencies and configures JSX/TSX transforms, React module aliases, and fast-refresh support for development 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.
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.
npx extension@latest create my-extension --template=content-react
Repository: extension-js/examples/content-react

new-react-router

new-react-router template screenshot React extension with client-side routing using React Router.
npx extension@latest create my-extension --template=new-react-router
Repository: extension-js/examples/new-react-router

Usage with an existing extension

Add React to an existing extension with the steps below.

Installation

Install React runtime dependencies: If you use TypeScript, also install React type packages:

Configuration

Common React file patterns:
  • JavaScript: *.jsx
  • TypeScript: *.tsx

Development behavior

In development mode, Extension.js enables React refresh integration when it detects React.
  • Extension.js uses react-refresh and @rspack/plugin-react-refresh for refresh behavior.
  • If your project lacks optional refresh dependencies, Extension.js installs them and asks for a restart.
  • Extension.js applies React aliases to 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