SSR и гидратация

Tippy.js — это мощная библиотека для создания всплывающих подсказок и тултипов в браузере. При использовании на клиенте она работает корректно «из коробки», но при интеграции в проекты с серверным рендерингом (SSR) и гидратацией возникают особенности, которые необходимо учитывать.


Особенности SSR

При SSR HTML контент формируется на сервере и отправляется готовым на клиент. В этом сценарии возникают следующие моменты:

  1. Отсутствие DOM на сервере Tippy.js напрямую взаимодействует с DOM: создаёт элементы подсказок, рассчитывает позиции и размеры. На сервере DOM отсутствует, поэтому попытка инициализации тултипов до гидратации вызовет ошибки.

  2. Нужна условная инициализация Инициализацию Tippy.js следует выполнять только на клиенте, после того как DOM готов. В React это обычно делают в useEffect:

    import { useEffect } from 'react';
    import tippy from 'tippy.js';
    import 'tippy.js/dist/tippy.css';
    
    function TooltipComponent() {
      useEffect(() => {
        tippy('#tooltip-target', {
          content: 'Пример подсказки',
          placement: 'top',
        });
      }, []);
    
      return <button id="tooltip-target">Наведи на меня</button>;
    }

    Здесь useEffect гарантирует, что инициализация произойдёт только на клиенте.

  3. Избегание рендеринга тултипов на сервере Любые динамические элементы Tippy.js не должны рендериться в HTML на сервере. Если требуется SSR-дружественный контент, можно отображать только целевой элемент, а тултип создавать уже на клиенте.


Гидратация

Гидратация — это процесс привязки клиентских скриптов к уже существующему серверному HTML. Для Tippy.js важны следующие нюансы:

  1. Совмещение с React/Vue/Svelte

    • В React/Tippy нужно инициировать тултипы после того, как компонент смонтирован (useEffect).
    • В Vue 3 можно использовать onMounted:
    import { onMounted } from 'vue';
    import tippy from 'tippy.js';
    
    export default {
      setup() {
        onMounted(() => {
          tippy('#tooltip-vue', {
            content: 'Тултип Vue',
          });
        });
      },
    };
  2. Гидратация сложного контента Tippy.js поддерживает HTML-контент и даже React-компоненты через render. Однако при SSR важно не рендерить эти элементы на сервере полностью. Пример использования React-подсказки с гидратацией:

    import ReactDOM from 'react-dom';
    import tippy, { createSingleton } from 'tippy.js';
    
    function MyTooltip() {
      return <div>Динамический контент</div>;
    }
    
    useEffect(() => {
      const instance = tippy('#tooltip-react', {
        render(reference) {
          const div = document.createElement('div');
          ReactDOM.render(<MyTooltip />, div);
          return {
            popper: div,
            reference,
          };
        },
      });
    
      return () => {
        instance.destroy();
      };
    }, []);

    Здесь ReactDOM.render выполняется только на клиенте, что обеспечивает корректную гидратацию.

  3. Избегание ошибок двойного рендеринга В SSR приложениях важно, чтобы тултип не создавался дважды: один раз сервером, второй раз клиентом. Использование условного рендеринга на клиенте (if (typeof window !== 'undefined')) предотвращает эту проблему.


Оптимизация для SSR

  • Lazy-инициализация: создаются только те тултипы, которые реально нужны пользователю, снижая нагрузку при гидратации.
  • Использование tippy.hideOnClick и trigger: позволяет контролировать момент отображения и минимизировать вмешательство в серверный HTML.
  • Создание singleton-инстансов: уменьшает количество DOM-узлов и оптимизирует производительность при гидратации большого количества тултипов.

Важные параметры Tippy.js при SSR

Параметр Рекомендации для SSR и гидратации
appendTo Использовать document.body на клиенте, не пытаться рендерить на сервере
interactive Включать только для клиентских тултипов, чтобы избежать ошибок позиционирования
animation Можно отключить или использовать только после гидратации для плавности
render Динамический контент следует создавать только на клиенте

Итог

Tippy.js в средах SSR требует строгого разделения серверного и клиентского рендеринга. Основная идея — не инициировать тултипы на сервере, создавать их динамически после гидратации и использовать условный код для работы с DOM. Это обеспечивает корректное позиционирование, интерактивность и минимизацию ошибок при переходе от SSR к клиентскому рендерингу.