How to Create a Side Panel for Chrome Extensions Using the React-Vite Boilerplate

The jonghakseo/chrome-extension-boilerplate-react-vite ships with a pre-configured side panel that requires only manifest verification and React component customization to activate.

The jonghakseo/chrome-extension-boilerplate-react-vite repository provides a complete, production-ready side panel implementation using React and Vite. To create a side panel for Chrome extensions with this boilerplate, you work within the existing pages/side-panel directory structure, customizing the React component while the build system handles Chrome manifest compliance and bundling automatically.

Understanding the Side Panel Architecture

The side panel implementation consists of three integrated layers that handle Chrome API compliance, UI rendering, and build optimization.

Manifest Declaration

Chrome requires explicit permission and path configuration to enable side panel functionality. In chrome-extension/manifest.ts, the boilerplate declares the side_panel field alongside the necessary permission:

side_panel: {
  default_path: 'side-panel/index.html',
},
permissions: ['sidePanel']

This tells Chrome to load side-panel/index.html from the extension's distribution folder when the user opens the side panel.

Page Assets

The UI layer resides in pages/side-panel/ and follows standard React patterns:

Build Configuration

The pages/side-panel/vite.config.mts file extends the shared withPageConfig helper from packages/vite-config/lib/with-page-config.ts. This configuration compiles TypeScript and React using SWC, outputs assets to dist/side-panel (matching the manifest path), excludes Chrome API from bundling, and applies Node polyfills for extension compatibility.

Customizing the Side Panel Component

Follow these steps to modify the existing side panel or create additional panels.

  1. Verify manifest configuration: Confirm chrome-extension/manifest.ts includes permissions: ['sidePanel'] and that side_panel.default_path points to your HTML file (default: 'side-panel/index.html').

  2. Modify the React component: Edit pages/side-panel/src/SidePanel.tsx or create a new component. The boilerplate provides shared hooks like useStorage and UI components from @extension/ui for consistent state management across extension surfaces.

  3. Update the entry point: If you rename the component, modify pages/side-panel/src/index.tsx to import and render your new component into the #app-container element.

  4. Apply styling: Tailwind CSS is pre-configured. Add utility classes directly in components or import custom CSS files via import '@src/CustomStyles.css';.

  5. Test locally: Run pnpm dev to start the Vite development server. Load the unpacked extension from dist/ in Chrome, then open the side panel via the toolbar button's context menu or Chrome's side panel UI.

  6. Build for production: Execute pnpm build to generate optimized assets in dist/side-panel/ that Chrome serves when the panel opens.

Code Implementation Examples

Creating a Custom Side Panel Component

Replace or extend pages/side-panel/src/SidePanel.tsx with your own implementation:

// File: pages/side-panel/src/MySidePanel.tsx
import '@src/MySidePanel.css';
import { useStorage } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import { cn, ToggleButton } from '@extension/ui';

const MySidePanel = () => {
  const { isLight } = useStorage(exampleThemeStorage);
  const logo = isLight ? 'side-panel/logo_vertical.svg' : 'side-panel/logo_vertical_dark.svg';

  return (
    <div className={cn('App', isLight ? 'bg-slate-50' : 'bg-gray-800')}>
      <header className={cn('App-header', isLight ? 'text-gray-900' : 'text-gray-100')}>
        <img src={chrome.runtime.getURL(logo)} className="App-logo" alt="logo" />
        <p>Welcome to my custom side panel!</p>
        <ToggleButton onClick={exampleThemeStorage.toggle}>Toggle Theme</ToggleButton>
      </header>
    </div>
  );
};

export default MySidePanel;

Wiring the Component to the Entry Script

Update pages/side-panel/src/index.tsx to mount your custom component:

// File: pages/side-panel/src/index.tsx
import '@src/index.css';
import MySidePanel from '@src/MySidePanel';
import { createRoot } from 'react-dom/client';

const app = document.querySelector('#app-container');
if (!app) throw new Error('Missing #app-container');

createRoot(app).render(<MySidePanel />);

Adjusting the Manifest Path

If you change the output directory or HTML filename, update chrome-extension/manifest.ts:

side_panel: {
  default_path: 'my-custom-panel/index.html',
},

Ensure the Vite config's outDir in pages/side-panel/vite.config.mts corresponds to this path within the dist folder.

Summary

  • The boilerplate includes a complete side panel setup in pages/side-panel/ with manifest declarations, React components, and Vite configuration.
  • Manifest configuration in chrome-extension/manifest.ts requires the sidePanel permission and side_panel.default_path pointing to the built HTML file.
  • Build integration via withPageConfig outputs to dist/side-panel, ensuring Chrome loads the correct assets.
  • Development workflow uses standard React patterns with pnpm dev for live reloading and pnpm build for production bundling.
  • Shared utilities from @extension/shared and @extension/ui provide consistent state management and styling across the side panel and other extension pages.

Frequently Asked Questions

Does this boilerplate support multiple side panels?

Chrome extensions currently support only one side panel per extension at a time. You can dynamically change the panel's content using React Router or conditional rendering within the single SidePanel.tsx component, but you cannot declare multiple side_panel entries in the manifest.

How do I access Chrome storage from the side panel?

Use the useStorage hook from @extension/shared as demonstrated in the boilerplate's SidePanel.tsx. This hook synchronizes with the exampleThemeStorage or any custom storage module you define in the storage package, providing reactive state across the popup, content scripts, and side panel.

Why does my side panel show a blank screen after building?

Verify that pages/side-panel/vite.config.mts outputs to the correct directory (../../dist/side-panel by default) and that chrome-extension/manifest.ts references the correct relative path (side-panel/index.html). A mismatch between the Vite outDir and the manifest default_path causes Chrome to fail loading the panel assets.

Can I use TypeScript path aliases in the side panel?

Yes. The boilerplate configures path aliases like @src and @extension/ui in the Vite configuration and TypeScript base configuration. These aliases work consistently across the side panel, popup, and options pages, allowing clean imports of shared components and utilities.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →