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:
- HTML entry:
pages/side-panel/index.htmlprovides the DOM mount point (#app-container) - Bootstrap script:
pages/side-panel/src/index.tsxcreates the React root and renders the component - UI component:
pages/side-panel/src/SidePanel.tsxcontains the actual React implementation
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.
-
Verify manifest configuration: Confirm
chrome-extension/manifest.tsincludespermissions: ['sidePanel']and thatside_panel.default_pathpoints to your HTML file (default:'side-panel/index.html'). -
Modify the React component: Edit
pages/side-panel/src/SidePanel.tsxor create a new component. The boilerplate provides shared hooks likeuseStorageand UI components from@extension/uifor consistent state management across extension surfaces. -
Update the entry point: If you rename the component, modify
pages/side-panel/src/index.tsxto import and render your new component into the#app-containerelement. -
Apply styling: Tailwind CSS is pre-configured. Add utility classes directly in components or import custom CSS files via
import '@src/CustomStyles.css';. -
Test locally: Run
pnpm devto start the Vite development server. Load the unpacked extension fromdist/in Chrome, then open the side panel via the toolbar button's context menu or Chrome's side panel UI. -
Build for production: Execute
pnpm buildto generate optimized assets indist/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.tsrequires thesidePanelpermission andside_panel.default_pathpointing to the built HTML file. - Build integration via
withPageConfigoutputs todist/side-panel, ensuring Chrome loads the correct assets. - Development workflow uses standard React patterns with
pnpm devfor live reloading andpnpm buildfor production bundling. - Shared utilities from
@extension/sharedand@extension/uiprovide 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →