Setting Up React for Chrome Extension Development

This guide covers building Chrome extensions with React, covering project initialization, architecture patterns, and best practices for modern extension development.

Recommended Stack {#recommended-stack}

Project Initialization {#project-initialization}

Create a new Vite project and add the CRXJS plugin:

npm create vite@latest my-extension -- --template react-ts
cd my-extension
npm install vite-plugin-chrome-extension

Configure vite.config.ts:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import crx from 'vite-plugin-chrome-extension';

export default defineConfig({
  plugins: [
    react(),
    crx({ manifest: manifestJson })
  ]
});

Multiple React Roots {#multiple-react-roots}

Chrome extensions have multiple entry points (popup, options page, side panel). Each should be a separate React app:

src/
 popup/       # Browser action popup
 options/     # Extension options page
 sidepanel/   # Side panel page
 content/     # Content scripts

Configure multiple HTML entry points in Vite and register them in manifest.json.

Shared Components {#shared-components}

Create a common UI library used across all extension contexts:

packages/ui/
 Button.tsx
 Input.tsx
 Modal.tsx

Publish as internal package or use workspace monorepo structure.

State Management {#state-management}

Zustand (Recommended) {#zustand-recommended}

Lightweight, no provider wrapper needed, works across extension contexts:

import { create } from 'zustand';

interface ExtensionStore {
  settings: Settings;
  updateSettings: (settings: Settings) => void;
}

export const useStore = create<ExtensionStore>((set) => ({
  settings: defaultSettings,
  updateSettings: (settings) => set({ settings }),
}));

Redux Toolkit {#redux-toolkit}

For complex state needs, Redux Toolkit provides standardized patterns.

Content Scripts with React {#content-scripts-with-react}

Content scripts run in page context. Use shadow DOM for style isolation:

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.createElement('div');
container.id = 'my-extension-root';
document.body.appendChild(container);

const shadow = container.attachShadow({ mode: 'open' });
const reactRoot = document.createElement('div');
shadow.appendChild(reactRoot);

createRoot(reactRoot).render(<App />);

React DevTools {#react-devtools}

React DevTools works in extension pages. Enable in manifest.json:

{
  "permissions": ["activeTab"]
}

Open DevTools on popup, options, or side panel pages to inspect React component trees.

Routing {#routing}

Use hash routing for extension pages since they run from chrome-extension://:

import { HashRouter, Routes, Route } from 'react-router-dom';

<HashRouter>
  <Routes>
    <Route path="/popup" element={<Popup />} />
    <Route path="/options" element={<Options />} />
  </Routes>
</HashRouter>

Styling Options {#styling-options}

Testing {#testing}

Custom Hooks for Extension APIs {#custom-hooks-for-extension-apis}

Create reusable hooks for common extension functionality:

// useStorage.ts
import { useState, useEffect } from 'react';
import { Storage } from 'webextension-polyfill';

export function useStorage<T>(key: string, defaultValue: T) {
  const [value, setValue] = useState<T>(defaultValue);

  useEffect(() => {
    Storage.local.get(key).then((result) => {
      setValue(result[key] ?? defaultValue);
    });
  }, [key]);

  const update = (newValue: T) => {
    Storage.local.set({ [key]: newValue });
    setValue(newValue);
  };

  return [value, update];
}

// useMessage.ts
import { useEffect } from 'react';
import { Runtime } from 'webextension-polyfill';

export function useMessage(callback: (message: any) => void) {
  useEffect(() => {
    const listener = (message: any) => callback(message);
    Runtime.onMessage.addListener(listener);
    return () => Runtime.onMessage.removeListener(listener);
  }, [callback]);
}

Performance Optimization {#performance-optimization}

Related Guides {#related-guides}

Related Articles {#related-articles}

Related Articles

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.