Реализация Side Panel браузерного расширения (Chrome)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Side Panel браузерного расширения (Chrome)
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация Side Panel браузерного расширения (Chrome)

Side Panel — боковая панель Chrome, появившаяся в Chrome 114. Она работает как постоянная панель сбоку от контента страницы, не закрывается при переключении вкладок и имеет значительно больше места, чем Popup. Это первый нативный механизм для «прикреплённых» интерфейсов в Chrome.

Отличия Side Panel от Popup

Характеристика Popup Side Panel
Ширина 800 px макс. ~400 px (фиксированная Chrome)
Жизненный цикл До потери фокуса Постоянная, переживает смену вкладок
Контекст вкладки Привязан к активной Может быть глобальной или per-tab
Доступность С Chrome 4 С Chrome 114
Firefox/Safari Нет аналога Нет аналога

Подключение в Manifest V3

{
  "manifest_version": 3,
  "name": "My Side Panel Extension",
  "version": "1.0.0",
  "permissions": ["storage", "tabs", "activeTab", "scripting", "sidePanel"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_icon": "icons/icon48.png",
    "default_title": "Открыть панель"
  },
  "side_panel": {
    "default_path": "panel/panel.html"
  }
}

Открытие панели по клику на иконку

По умолчанию клик на иконку расширения ничего не делает с Side Panel. Нужно явно настроить поведение:

// background.js (Service Worker)

// Вариант 1: открывать панель при каждом клике на иконку
chrome.sidePanel
  .setPanelBehavior({ openPanelOnActionClick: true })
  .catch(console.error);

// Вариант 2: открывать программно (требует user gesture)
chrome.action.onClicked.addListener(async (tab) => {
  await chrome.sidePanel.open({ tabId: tab.id });
});

Per-tab vs глобальная панель

Панель может быть разной для каждой вкладки или общей для всех:

// background.js

// Разная панель в зависимости от сайта
chrome.tabs.onUpdated.addListener(async (tabId, info, tab) => {
  if (info.status !== 'complete') return;

  if (tab.url?.includes('github.com')) {
    await chrome.sidePanel.setOptions({
      tabId,
      path: 'panel/github-panel.html',
      enabled: true,
    });
  } else if (tab.url?.includes('figma.com')) {
    await chrome.sidePanel.setOptions({
      tabId,
      path: 'panel/design-panel.html',
      enabled: true,
    });
  } else {
    // Выключить панель для обычных страниц
    await chrome.sidePanel.setOptions({ tabId, enabled: false });
  }
});

React-приложение в Side Panel

Структура panel/panel.html аналогична Popup, но имеет больше пространства:

// panel/App.tsx
import { useEffect, useState, useRef } from 'react';
import browser from 'webextension-polyfill';

export function SidePanel() {
  const [notes, setNotes] = useState<string[]>([]);
  const [currentUrl, setCurrentUrl] = useState('');
  const portRef = useRef<browser.Runtime.Port | null>(null);

  useEffect(() => {
    // Постоянное соединение с background для стриминга данных
    portRef.current = browser.runtime.connect({ name: 'side-panel' });

    portRef.current.onMessage.addListener((msg) => {
      if (msg.type === 'PAGE_CHANGED') {
        setCurrentUrl(msg.url);
        loadNotesForUrl(msg.url);
      }
      if (msg.type === 'SELECTION_CHANGED') {
        // Пользователь выделил текст на странице
        handleNewSelection(msg.text);
      }
    });

    // Загрузить начальное состояние
    browser.tabs.query({ active: true, currentWindow: true }).then(([tab]) => {
      if (tab.url) {
        setCurrentUrl(tab.url);
        loadNotesForUrl(tab.url);
      }
    });

    return () => portRef.current?.disconnect();
  }, []);

  async function loadNotesForUrl(url: string) {
    const domain = new URL(url).hostname;
    const { notes } = await browser.storage.local.get(`notes_${domain}`);
    setNotes(notes ?? []);
  }

  async function addNote(text: string) {
    const domain = new URL(currentUrl).hostname;
    const updated = [...notes, text];
    setNotes(updated);
    await browser.storage.local.set({ [`notes_${domain}`]: updated });
  }

  return (
    <div className="side-panel">
      <header className="side-panel__header">
        <h2>Заметки</h2>
        <span className="side-panel__url">{new URL(currentUrl).hostname}</span>
      </header>

      <div className="side-panel__notes">
        {notes.map((note, i) => (
          <div key={i} className="note">{note}</div>
        ))}
      </div>

      <NoteInput onAdd={addNote} />
    </div>
  );
}

Связь с Content Script через Port

Side Panel живёт долго — удобно использовать постоянные соединения вместо разовых sendMessage:

// background.js
const sidePanelPorts = new Map(); // tabId -> Port

chrome.runtime.onConnect.addListener((port) => {
  if (port.name !== 'side-panel') return;

  // Определяем, к какой вкладке относится панель
  chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
    sidePanelPorts.set(tab.id, port);

    port.onDisconnect.addListener(() => {
      sidePanelPorts.delete(tab.id);
    });
  });
});

// Когда content script присылает событие — пересылаем в панель
chrome.runtime.onMessage.addListener((msg, sender) => {
  if (msg.type === 'SELECTION_CHANGED') {
    const port = sidePanelPorts.get(sender.tab?.id);
    port?.postMessage({ type: 'SELECTION_CHANGED', text: msg.text });
  }
});
// content.js — следит за выделением текста
document.addEventListener('mouseup', () => {
  const selection = window.getSelection()?.toString().trim();
  if (selection && selection.length > 3) {
    chrome.runtime.sendMessage({ type: 'SELECTION_CHANGED', text: selection });
  }
});

Определение смены вкладки

Side Panel должен реагировать на переключение вкладок пользователем:

// background.js
chrome.tabs.onActivated.addListener(async ({ tabId }) => {
  const tab = await chrome.tabs.get(tabId);
  const port = sidePanelPorts.get(tabId);
  port?.postMessage({ type: 'PAGE_CHANGED', url: tab.url, title: tab.title });
});

chrome.tabs.onUpdated.addListener((tabId, info, tab) => {
  if (info.status !== 'complete') return;
  const port = sidePanelPorts.get(tabId);
  port?.postMessage({ type: 'PAGE_CHANGED', url: tab.url, title: tab.title });
});

Сроки

Side Panel с React, постоянным соединением к background, синхронизацией с вкладками и хранением данных — 3–5 рабочих дней. Панель с реалтайм-взаимодействием с контентом страницы, историей и поиском — 6–9 дней.