Создание Chrome Extension с React, TypeScript и Vite
Пошаговое руководство по созданию и публикации Chrome расширения с использованием React 18, TypeScript, Vite и Manifest V3.
В этой статье мы создадим Chrome расширение с нуля, используя React, TypeScript и Vite. Построим Freelance Rate Calculator — калькулятор рыночных ставок для разработчиков.
Статья включает:
- Настройка проекта React + Vite + TypeScript для Chrome Extension
- Конфигурация manifest.json для Manifest V3
- Создание UI калькулятора с Tailwind CSS
- Локальная загрузка и тестирование расширения
- Публикация в Chrome Web Store
Настройка проекта
Создаём новый Vite проект с React и TypeScript:
npm create vite@latest rate-calculator -- --template react-ts
cd rate-calculator
npm installДобавляем Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -pНастраиваем tailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};Добавляем директивы Tailwind в src/index.css:
@tailwind base;
@tailwind components;
@tailwind utilities;Добавляем Lucide иконки:
npm install lucide-reactДобавляем Manifest V3
Создаём public/manifest.json:
{
"manifest_version": 3,
"name": "Freelance Rate Calculator",
"version": "1.0.0",
"description": "Calculate your freelance rate based on tech stack, experience, and location",
"action": {
"default_popup": "index.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}Создаём папку public/icons/ и добавляем иконки расширения в размерах 16x16, 48x48 и 128x128.
Добавляем данные ставок
Создаём src/data/rates.ts с технологиями, уровнями опыта и локациями:
export interface TechStack {
id: string;
name: string;
baseRate: number;
demand: "high" | "medium" | "low";
}
export interface ExperienceLevel {
id: string;
name: string;
years: string;
multiplier: number;
}
export interface Location {
id: string;
name: string;
multiplier: number;
}
export const techStacks: TechStack[] = [
{ id: "genai", name: "GenAI / LLM", baseRate: 175, demand: "high" },
{ id: "rust", name: "Rust", baseRate: 135, demand: "high" },
{ id: "react", name: "React", baseRate: 90, demand: "high" },
{ id: "typescript", name: "TypeScript", baseRate: 92, demand: "high" },
{ id: "nodejs", name: "Node.js", baseRate: 85, demand: "high" },
{ id: "python", name: "Python", baseRate: 90, demand: "high" },
// добавьте больше стеков...
];
export const experienceLevels: ExperienceLevel[] = [
{ id: "junior", name: "Junior", years: "0-2 года", multiplier: 0.55 },
{ id: "mid", name: "Middle", years: "3-5 лет", multiplier: 1.0 },
{ id: "senior", name: "Senior", years: "5-8 лет", multiplier: 1.45 },
{ id: "lead", name: "Lead / Architect", years: "8+ лет", multiplier: 1.85 },
];
export const locations: Location[] = [
{ id: "usa", name: "США", multiplier: 1.0 },
{ id: "uk", name: "Великобритания", multiplier: 0.85 },
{ id: "germany", name: "Германия", multiplier: 0.82 },
{ id: "japan", name: "Япония", multiplier: 0.62 },
{ id: "india", name: "Индия", multiplier: 0.32 },
// добавьте больше стран...
];Формула расчёта ставки:
const getMarketRate = (
stack: TechStack,
exp: ExperienceLevel,
loc: Location
) => {
const base = stack.baseRate * exp.multiplier * loc.multiplier;
return {
low: Math.round(base * 0.85),
mid: Math.round(base),
high: Math.round(base * 1.15),
};
};Пример: Senior React разработчик в Германии = 90 * 1.45 * 0.82 = $107/час
Создаём UI калькулятора
Обновляем src/App.tsx с формой калькулятора:
import { useState } from "react";
import { techStacks, experienceLevels, locations } from "./data/rates";
import type { TechStack, ExperienceLevel, Location } from "./data/rates";
function App() {
const [stack, setStack] = useState<TechStack | null>(null);
const [experience, setExperience] = useState<ExperienceLevel | null>(null);
const [location, setLocation] = useState<Location | null>(null);
const [myRate, setMyRate] = useState("");
const [showResult, setShowResult] = useState(false);
const getMarketRate = () => {
if (!stack || !experience || !location) return null;
const base = stack.baseRate * experience.multiplier * location.multiplier;
return {
low: Math.round(base * 0.85),
mid: Math.round(base),
high: Math.round(base * 1.15),
};
};
const handleCheck = () => {
if (stack && experience && location && myRate) {
setShowResult(true);
}
};
// рендерим форму или результаты в зависимости от showResult
return (
<div className="w-[400px] h-[600px] p-4 bg-gray-900 text-white">
{/* форма и результаты */}
</div>
);
}
export default App;Устанавливаем фиксированные размеры popup в src/index.css:
body {
width: 400px;
height: 600px;
margin: 0;
overflow: hidden;
}Форма ввода с dropdown для стека, опыта и локации:

Экран результатов со сравнением рыночных ставок:

Сборка расширения
Обновляем vite.config.ts для сборки расширения:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
build: {
outDir: "dist",
rollupOptions: {
input: "index.html",
},
},
});Собираем расширение:
npm run buildЭто создаёт папку dist/ с файлами расширения.
Загрузка расширения локально
Открываем Chrome и переходим на chrome://extensions. Включаем "Режим разработчика" в правом верхнем углу.
Нажимаем "Загрузить распакованное расширение" и выбираем папку dist/.

Нажимаем на иконку расширения в панели инструментов для тестирования popup.
Публикация в Chrome Web Store
Переходим в Chrome Web Store Developer Dashboard.
При первой публикации требуется единоразовая оплата $5.
Подготавливаем ассеты:
- Иконка расширения: 128x128 PNG
- Скриншот: 1280x800 или 640x400
- Промо-плитка: 440x280
Создаём ZIP папки dist/:
cd dist && zip -r ../extension.zip . && cd ..Загружаем ZIP, заполняем описание, добавляем скриншоты, отправляем на проверку.
Проверка занимает 1-3 дня. Частые причины отклонения:
- Переспам ключевых слов в описании
- Отсутствие политики конфиденциальности (при использовании разрешений)
- Некорректные скриншоты
После одобрения:

Заключение
Готово. Мы создали Chrome расширение с React, TypeScript и Vite, протестировали локально и опубликовали в Chrome Web Store.
Финальный стек:
- React 18
- TypeScript
- Vite
- Tailwind CSS
- Manifest V3
Расширение: Freelance Rate Calculator
Понравилась статья? Подписывайтесь на Twitter @Nefayran — #buildinpublic контент.
