logo
BlogsMiscellaneous

Building a Chrome Extension with React, Vite, and Tailwind using CRXJS

# chrome-extension# tailwind# javascript# react# shadow-dom# crxjs# html

If you know HTML, CSS, and JavaScript, you're already equipped to build Chrome extensions! And if you're familiar with React, even better - we can create a powerful extension using modern tools and best practices.

Getting Started

First, let's create a new Vite project:

npm create vite@latest my-extension --template react-ts

After creating your project, we need the CRXJS Vite plugin for Chrome extension development:

npm install @crxjs/vite-plugin

This plugin is crucial as it provides hot reloading functionality - your extension will update automatically as you code, without manual refreshing and building everytime.

Essential Configuration

Let's set up the Vite configuration first:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { crx } from '@crxjs/vite-plugin'
import manifest from './manifest.json'
import { resolve } from 'path'

export default defineConfig({
  plugins: [
    react(),
    crx({ 
      manifest,
      contentScripts: {
        injectCss: true,
      },
    }),
  ],
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
})

Next, create your manifest file:

// manifest.json
{
  "manifest_version": 3,
  "name": "My First Extension",
  "version": "1.0",
  "description": "A sample extension to demonstrate core files",
  "permissions": ["tabs", "storage"],
  "action": {
    "default_popup": "src/popup/index.html"
  },
  "content_scripts": [
    {
      "js": ["src/content/index.tsx"]
    }
  ]
}

Core Files Structure

Here's the essential folder structure for your extension:

vite.config.ts
manifest.json
src
├── assets
│   └── styles
│       └── index.css
├── background
│   └── index.ts
├── content
│   ├── content.tsx
│   └── index.tsx
├── popup
│   ├── index.html
│   ├── index.tsx
│   └── popup.tsx
└── utils
    └── create-shadow-root.tsx

Want to understand the core files use case better? For a detailed explanation of each file's purpose and use cases, check out A Deep Dive into Chrome Extension Architecture: Understanding the Core Files

Let's break down each core component:

1. Background Script

// src/background/index.ts
console.log('background script is activated')

2. Content Scripts

// src/content/content.tsx
import React from 'react'

const Content = () => {
  return (
    <div>Content</div>
  )
}

export default Content

// src/content/index.tsx
import styles from '../assets/styles/index.css?inline'
import createShadowRoot from '../utils/create-shadow-root'
import Content from './content'

const root = createShadowRoot(styles)
root.render(<Content/>)

3. Popup Interface

<!-- src/popup/index.html -->
<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Extension Popup</title>
</head>
<body>
  <div id="ext-popup-page"></div>
  <script type="module" src="./index.tsx"></script>
</body>
</html>
// src/popup/popup.tsx
import React from 'react'

const Popup = () => {
  return (
    <div>Popup</div>
  )
}

export default Popup

// src/popup/index.tsx
import styles from "../assets/styles/index.css?inline"
import createShadowRoot from "../utils/create-shadow-root"
import PopUp from "./popup"

const root = createShadowRoot(styles)
root.render(<PopUp />)

Shadow DOM Integration

One crucial aspect that many developers overlook is proper CSS isolation. Here's our shadow root utility:

// src/utils/create-shadow-root.tsx
import { createRoot } from 'react-dom/client'

export default function createShadowRoot(styles: string) {

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

  const globalStyleSheet = new CSSStyleSheet()
  globalStyleSheet.replaceSync(styles)
  shadow.adoptedStyleSheets = [globalStyleSheet]

  document.body.appendChild(container)
  return createRoot(shadow)

}

This Shadow DOM implementation is crucial because:

  1. It isolates your extension's styles from the host website

  2. Prevents CSS conflicts and style leakage

  3. Ensures consistent styling across different websites

  4. Protects your extension's UI from host page style changes

Pro Tips

  • Always use the Shadow DOM for content scripts

  • Keep your manifest.json clean and only request necessary permissions

  • Use TypeScript for better code maintainability

  • Leverage hot reloading during development

  • Organize your code into clear, separate concerns (popup, content, background)

That's it! You now have a solid foundation for building a modern Chrome extension with React. The setup handles all the core functionality while maintaining clean separation of concerns and proper style isolation.

Subscribe for our newsletter

Comments







© 2024 Developerthink. All Rights Reserved.