Работа с серверным рендерингом

Headroom.js — это лёгкая библиотека для управления поведением шапки страницы при скролле. Она основывается на прямом взаимодействии с DOM, что создаёт ряд особенностей при использовании с серверным рендерингом (SSR) в таких фреймворках, как Next.js, Nuxt.js или других решениях на основе Node.js. В условиях SSR необходимо учитывать отсутствие реального окна браузера на этапе генерации HTML.


Инициализация в SSR

При серверной генерации страницы объекты window и document недоступны. Прямой вызов new Headroom(element) на сервере приведёт к ошибке. Чтобы избежать этого:

let headroom;

if (typeof window !== "undefined") {
  const element = document.querySelector(".header");
  headroom = new Headroom(element, {
    tolerance: 5,
    offset: 50,
    classes: {
      pinned: "header--pinned",
      unpinned: "header--unpinned",
      top: "header--top",
      notTop: "header--not-top"
    }
  });
  headroom.init();
}

Ключевой момент — проверка typeof window !== "undefined". Это гарантирует, что код Headroom.js выполнится только в браузере, а серверная сборка останется безопасной.


Отложенная инициализация

Иногда требуется более тонкая интеграция с жизненным циклом компонентов. В React или Vue рекомендуется использовать хуки или lifecycle методы:

React (Next.js):

import { useEffect, useRef } from "react";
import Headroom from "headroom.js";

export default function Header() {
  const headerRef = useRef(null);

  useEffect(() => {
    if (headerRef.current) {
      const headroom = new Headroom(headerRef.current, {
        tolerance: 5,
        offset: 50
      });
      headroom.init();
      return () => headroom.destroy();
    }
  }, []);

  return <header ref={headerRef} className="header">Мой сайт</header>;
}

Vue 3:

import { onMounted, ref } from "vue";
import Headroom from "headroom.js";

export default {
  setup() {
    const header = ref(null);

    onMounted(() => {
      if (header.value) {
        const headroom = new Headroom(header.value, { tolerance: 5, offset: 50 });
        headroom.init();
      }
    });

    return { header };
  }
};

Использование useEffect и onMounted гарантирует запуск Headroom.js только на клиенте, после того как DOM доступен.


Управление классами и состояниями

Headroom.js добавляет CSS-классы к элементу в зависимости от положения скролла:

  • headroom--pinned — шапка зафиксирована, видна.
  • headroom--unpinned — шапка скрыта при скролле вниз.
  • headroom--top — скролл находится в верхней точке страницы.
  • headroom--not-top — страница прокручена вниз.

При SSR эти классы не добавляются на сервере, поэтому важно обеспечить корректные стили для начального состояния. Рекомендуется добавить базовый класс, который отражает дефолтное состояние шапки:

<header class="header header--top">...</header>

Интеграция с CSS-анимациями

Для плавного появления и скрытия шапки удобно использовать CSS-переходы:

.header {
  transition: transform 0.3s ease-in-out;
}

.header--unpinned {
  transform: translateY(-100%);
}

.header--pinned {
  transform: translateY(0);
}

Поскольку на сервере шапка будет рендериться с классом header--top, а остальные состояния активируются только на клиенте, визуальный переход не ломается.


Проблемы с гидратацией

Если шапка на сервере рендерится с дефолтным классом, а при первом рендере на клиенте Headroom сразу меняет класс из-за позиции скролла, возможны мигания (flash). Решение:

  1. Добавить opacity: 0 на шапку в начальном состоянии.
  2. После инициализации Headroom на клиенте выставлять opacity: 1 через JavaScript или CSS-анимацию.
header.style.opacity = "1";

Совместимость с SPA и SSR

При разработке гибридных приложений важно:

  • Отделять инициализацию Headroom от серверного рендера.
  • Использовать клиентские хуки (useEffect, onMounted) для запуска библиотеки.
  • Обеспечивать корректное начальное состояние через CSS, чтобы серверный рендер выглядел так же, как ожидается на клиенте.

Настройка параметров Headroom.js в SSR

Параметры библиотеки работают одинаково, но стоит учитывать особенности:

  • offset — учитывается после гидратации, на сервере не имеет значения.
  • tolerance — чувствительность к скроллу.
  • classes — желательно полностью настроить для управления стилями, так как на сервере базовый класс нужен для корректного визуального отображения.
const options = {
  tolerance: 10,
  offset: 100,
  classes: {
    pinned: "header--pinned",
    unpinned: "header--unpinned",
    top: "header--top",
    notTop: "header--not-top",
    initial: "header--top" // начальный класс при SSR
  }
};

Использование свойства initial позволяет задать состояние шапки сразу на сервере, предотвращая рассинхрон визуального состояния.


Вывод

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