MIT License
This project is listed in the Awesome Vite
This boilerplate is made for creating chrome extensions using React and Typescript.
The focus was on improving the build speed and development experience with Vite.
extensionDescription
and extensionName
in messages.jsonnpm install -g pnpm
(check your node version >= 16.6, recommended >= 18)pnpm install
pnpm dev
or npm run dev
pnpm build
or npm run build
chrome://extensions
Developer mode
Load unpacked extension
dist
folderpnpm dev:firefox
or npm run dev:firefox
pnpm build:firefox
or npm run build:firefox
about:debugging#/runtime/this-firefox
Load Temporary Add-on...
manifest.json
from dist
folderIMPORTANT: If you DO NOT want to use css file in the content script, you need to delete the css file in your manifest.js
content_scripts: [
{
// YOU NEED TO DELETE THIS
css: ["assets/css/contentStyle<KEY>.chunk.css"]
}
];
The smallest, fastest, most feature complete Tailwind-in-JS solution in existence
1. Install the library:
$ pnpm install -D @twind/core @twind/preset-autoprefix @twind/preset-tailwind
2. Create twind.config.ts in the root folder
import { defineConfig } from '@twind/core';
import presetTailwind from '@twind/preset-tailwind';
import presetAutoprefix from '@twind/preset-autoprefix';
export default defineConfig({
presets: [presetAutoprefix(), presetTailwind()],
});
3. Create src/shared/style/twind.ts for importing
import { twind, cssom, observe } from '@twind/core';
import 'construct-style-sheets-polyfill';
import config from '@root/twind.config';
export function attachTwindStyle<T extends { adoptedStyleSheets: unknown }>(
observedElement: Element,
documentOrShadowRoot: T,
) {
const sheet = cssom(new CSSStyleSheet());
const tw = twind(config, sheet);
observe(tw, observedElement);
documentOrShadowRoot.adoptedStyleSheets = [sheet.target];
}
4. You can use Tailwind in your project:
import { attachTwindStyle } from '@src/shared/style/twind';
...
attachTwindStyle(appContainer, document);
const root = createRoot(appContainer);
root.render(<Popup />);
5. If you want to use Twind in the content script, you need to add the following code:
import { attachTwindStyle } from '@src/shared/style/twind';
...
attachTwindStyle(rootIntoShadow, shadowRoot);
createRoot(rootIntoShadow).render(<App />);
1. Install the library:
$ pnpm install @chakra-ui/react @emotion/cache @emotion/react
2. You should add the code to vite.config.ts
for Ignore unnecessary warnings
export default defineConfig({
build: {
rollupOptions: {
// Add below code ~~~~~
onwarn(warning, warn) {
if (
warning.code === "MODULE_LEVEL_DIRECTIVE" &&
warning.message.includes(`"use client"`)
) {
return;
}
warn(warning);
},
// Add above code ~~~~
},
},
});
3. You can use Chakra UI in your project:
import { Button } from "@chakra-ui/react";
export default function Popup() {
return <Button colorScheme="teal">Button</Button>;
}
if you don't want to use Chakra UI in the content script, you can skip 4,5 step.
4. If you want to use Chakra UI in the content script, you need to add the following code:
import { ReactNode, useCallback, useEffect, useState } from "react";
import {
ColorMode,
ColorModeContext,
ColorModeScript,
CSSReset,
extendTheme,
GlobalStyle,
ThemeProvider
} from "@chakra-ui/react";
const theme = extendTheme();
const getCurrentTheme = () => {
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return isDark ? "dark" : "light";
};
type CustomChakraProviderProps = {
shadowRootId: string;
children: ReactNode;
};
export default function CustomChakraProvider({ children, shadowRootId }: CustomChakraProviderProps) {
const [colorMode, setColorMode] = useState<ColorMode>(getCurrentTheme());
useEffect(() => {
const darkThemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const onChangeColorSchema = (event: MediaQueryListEvent) => {
const isDark = event.matches;
setColorMode(isDark ? "dark" : "light");
};
darkThemeMediaQuery.addEventListener("change", onChangeColorSchema);
return () => {
darkThemeMediaQuery.removeEventListener("change", onChangeColorSchema);
};
}, []);
const toggleColorMode = useCallback(() => {
setColorMode(prev => (prev === "dark" ? "light" : "dark"));
}, []);
return (
<ThemeProvider theme={{ ...theme, config: { ...theme.config, colorMode } }} cssVarsRoot={`#${shadowRootId}`}>
<ColorModeScript initialColorMode="system" />
<ColorModeContext.Provider value={{ colorMode, setColorMode, toggleColorMode }}>
<CSSReset />
<GlobalStyle />
{children}
</ColorModeContext.Provider>
</ThemeProvider>
);
}
import createCache from '@emotion/cache';
import { CacheProvider, type EmotionCache } from '@emotion/react';
import { ReactNode, useEffect, useState } from 'react';
export default function EmotionCacheProvider({ children, rootId }: { rootId: string; children: ReactNode }) {
const [emotionCache, setEmotionCache] = useState<EmotionCache | null>(null);
useEffect(() => {
function setEmotionStyles(shadowRoot: ShadowRoot) {
setEmotionCache(
createCache({
key: rootId,
container: shadowRoot,
}),
);
}
const root = document.getElementById(rootId);
if (root && root.shadowRoot) {
setEmotionStyles(root.shadowRoot);
}
}, []);
return emotionCache ? <CacheProvider value={emotionCache}>{children}</CacheProvider> : null;
}
5. Fix the src/pages/content/ui/root.tsx
file:
import CustomChakraProvider from '@pages/content/ui/CustomChakraProvider';
import EmotionCacheProvider from '@pages/content/ui/EmotionCacheProvider';
// ...
createRoot(rootIntoShadow).render(
// Add Providers
<EmotionCacheProvider rootId={root.id}>
<CustomChakraProvider shadowRootId={rootIntoShadow.id}>
<App />
</CustomChakraProvider>
</EmotionCacheProvider>,
);
Override Chrome pageschrome_url_overrides.newtab
in
manifest.json
Browser actionsaction.default_popup
in
manifest.json
Devtoolsdevtools_page
in manifest.json
Backgroundbackground.service_worker
in
manifest.json
Content Script (contentInjected/contentUI)content_scripts
in
manifest.json
Optionsoptions_page
in manifest.json
SidePanelside_panel.default_path
in
manifest.json
Black | White |
---|---|
Jetbrains | Jackson Hong |
---|---|