Правильный SSR для таймзон: решаем проблему с помощью @epic-web/client-hints 🌍⏰
Как @epic-web/client-hints передаёт таймзону через cookies для корректного SSR дат без мерцания и ошибок гидратации.

Вечная проблема при SSR-рендеринге дат — сервер понятия не имеет, в какой часовой зоне находится пользователь.
В итоге у нас обычно два так себе варианта:
- Рендерить всё на сервере в UTC, а на клиенте «чинить» через
useEffect. Получаем неприятный Flash of Incorrect Content (FOIC) или hydration error. - Не рендерить даты на сервере вообще и показывать skeleton/loading. Прощай, быстрый First Contentful Paint.
Решить эту проблему помогает библиотека @epic-web/client-hints (от Кента Доддса и команды Epic Web).
Как это работает?
Библиотека использует паттерн Client Hints через Cookies:
- Первый заход: До гидратации на клиенте выполняется крошечный скрипт. Он определяет таймзону пользователя через
Intl.DateTimeFormat().resolvedOptions().timeZone, записывает её в Cookie и перезагружает страницу (это происходит единоразово при самом первом посещении). - Все последующие запросы: Сервер читает куку в
loader(например, в React Router / Remix / Next) и сразу отдаёт HTML с идеально отформатированной датой под локаль и часовой пояс юзера.
В чём профит?
- Форматирование в loader: Можно форматировать даты прямо на бэкенде в
loaderи слать на клиент уже готовые строки. Меньше JS на клиенте, выше производительность. - Без гидро-ошибок: Идеальное совпадение HTML сервера и клиента.
- Не только таймзоны: Через
getHintUtilsточно так же можно пробрасывать на сервер предпочтения по цветовой теме (prefers-color-scheme), настройки accessibility (prefers-reduced-motion) и размеры экрана.
А как вы решаете задачу с локальными датами при SSR? Согласны на однократный релоад при первом визите ради идеального HTML, или до сих пор правм даты на клиенте через hydration? 👇