How to Set Up and Use Tailwind CSS for Styling Chrome Extension Pages
The chrome-extension-boilerplate-react-vite provides a shared Tailwind configuration automatically merged into every extension UI page via the withUI helper, generating isolated, production-ready CSS bundles during the Vite build process.
Setting up Tailwind CSS for Chrome extension pages requires handling isolated contexts, per-page scoping, and Manifest V3 constraints. The jonghakseo/chrome-extension-boilerplate-react-vite repository implements a centralized architecture that lets you set up and use Tailwind CSS across popups, side panels, options pages, and devtools panels without configuration duplication.
How the Tailwind CSS Architecture Works
The boilerplate distributes Tailwind through a multi-layer configuration system that keeps global design tokens consistent while allowing per-page customization.
Global Configuration Layer
The foundation resides in packages/tailwindcss-config/tailwind.config.ts. This file establishes the base theme, color palette, spacing scale, and plugins shared across every extension page. When you modify this file, changes propagate to all UI components automatically.
The withUI Helper Function
Located in packages/ui/lib/with-ui.ts, the withUI utility performs a deep merge between the global Tailwind config and page-specific overrides. This function ensures that customizations in individual pages extend rather than replace the shared design system, preventing configuration drift.
Per-Page Configuration
Each extension page contains a minimal tailwind.config.ts that imports withUI and defines its content paths. For example, pages/side-panel/tailwind.config.ts specifies which files Tailwind should scan for class names:
import { withUI } from '@extension/ui';
export default withUI({
content: ['index.html', 'src/**/*.tsx'],
});
This isolation ensures Tailwind only includes utilities actually used in that specific page, keeping CSS payloads minimal.
CSS Entry Points and Directives
Every page imports the shared CSS entry point in its local stylesheet. In pages/side-panel/src/index.css:
@import '@extension/ui/global.css';
The packages/ui/global.css file contains the standard Tailwind directives:
@tailwind base;
@tailwind components;
@tailwind utilities;
Vite Build Integration
During the build process, packages/vite-config/lib/build-content-script.ts executes the Tailwind CLI against each page's index.css. The withTw flag defaults to true, ensuring CSS compilation happens automatically. The generated stylesheets emit to dist/<page>/index.css alongside the JavaScript bundles, satisfying Manifest V3's requirement for static assets.
Configuring Tailwind for a New Extension Page
To set up Tailwind CSS when adding a new extension page:
- Create a
tailwind.config.tsin your new page directory that callswithUI:
import { withUI } from '@extension/ui';
export default withUI({
content: ['index.html', 'src/**/*.{tsx,html}'],
});
- Import the global CSS in your page's entry stylesheet:
@import '@extension/ui/global.css';
- Use Tailwind utilities directly in React components:
export default function NewPage() {
return (
<div className="flex flex-col gap-4 p-6 bg-slate-50">
<h1 className="text-xl font-semibold text-gray-900">
Extension Page Title
</h1>
<button className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
Submit
</button>
</div>
);
}
The Vite dev server watches tailwind.config.ts and CSS files, recompiling automatically when you run pnpm dev.
Benefits of This Setup for Chrome Extensions
Per-Page Isolation – Each page maintains its own content configuration, so the popup only ships CSS for popup components, and the side panel only ships side-panel styles. This prevents the CSS bloat common in monolithic Tailwind setups.
Shared Design System – The global config in packages/tailwindcss-config/tailwind.config.ts acts as a single source of truth for brand colors, typography, and spacing. The withUI helper guarantees every page inherits these tokens without manual synchronization.
Zero Runtime Overhead – Tailwind compiles to plain CSS during the Vite build. The extension ships static CSS files referenced in manifest.json, adhering to Manifest V3 security policies that prohibit runtime script injection or eval-based style generation.
Summary
- The boilerplate stores global Tailwind settings in
packages/tailwindcss-config/tailwind.config.ts. - The
withUIhelper inpackages/ui/lib/with-ui.tsmerges global and page-specific configurations. - Each page defines its own
tailwind.config.tspointing to local markup files for optimal CSS pruning. - Pages import
@extension/ui/global.cssto receive Tailwind directives. - The Vite configuration in
packages/vite-config/lib/build-content-script.tsautomatically compiles Tailwind during both development and production builds.
Frequently Asked Questions
How do I customize the global Tailwind theme for all extension pages?
Modify packages/tailwindcss-config/tailwind.config.ts to add custom colors, fonts, or spacing scales. Because every page's config merges with this base file through withUI, your theme changes apply immediately across popups, options pages, and side panels without editing individual page configurations.
Why does each page need its own tailwind.config.ts instead of one root config?
Individual configs allow precise control over the content array. By scoping Tailwind's scanner to only the files used by that specific extension page—such as pages/popup/src/**/*.tsx—the build system generates minimal CSS bundles. This architectural choice prevents shipping styles for the side panel inside the popup bundle, keeping extension package sizes small.
How do I add a Tailwind plugin to just one specific page?
Pass the plugin directly to withUI in that page's local tailwind.config.ts. The deep-merge algorithm in packages/ui/lib/with-ui.ts combines page-specific plugins with the global plugin array. For example, adding a forms plugin to only the options page keeps the popup bundle lean while enhancing the options UI.
Does this Tailwind setup work with content scripts?
The current architecture targets extension UI pages (popup, options, devtools, side-panel) through the Vite page build system. Content scripts require a different approach since they inject into arbitrary web pages, but the boilerplate's build-content-script.ts infrastructure can be extended to process Tailwind for content script CSS if you configure a separate entry point and content array specific to those components.
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 →