Next.js особенности

Radix UI представляет собой набор полностью доступных компонентов интерфейса, реализованных как headless-компоненты, что позволяет разработчикам полностью контролировать стилизацию и поведение. При использовании с Next.js важно учитывать особенности серверного рендеринга (SSR) и маршрутизации, чтобы компоненты корректно отображались как на сервере, так и на клиенте.

Компоненты Radix UI предоставляют минимальную оболочку и требуют стилизации с помощью CSS или CSS-in-JS. В Next.js это удобно комбинировать с библиотеками вроде Tailwind CSS, Stitches или Vanilla Extract, что обеспечивает высокую гибкость и поддерживаемость.

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

Next.js по умолчанию рендерит страницы на сервере, а затем “гидратирует” их на клиенте. Radix UI использует React и зависит от состояния компонента (например, открытое меню или диалог), что может привести к проблемам при несоответствии состояния на сервере и клиенте. Основные рекомендации:

  • Использовать проверку typeof window !== 'undefined' для рендеринга компонентов, зависящих от браузерного окружения.
  • Применять useEffect для действий, которые должны выполняться только на клиенте (например, динамическая установка фокуса).
  • Избегать прямого доступа к document или window при SSR без проверки.

Пример безопасного рендеринга модального окна:

import { Dialog } from '@radix-ui/react-dialog';
import { useState, useEffect } from 'react';

export default function ClientOnlyDialog() {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) return null;

  return (
    <Dialog>
      <Dialog.Trigger>Открыть диалог</Dialog.Trigger>
      <Dialog.Content>
        <p>Содержимое диалога</p>
        <Dialog.Close>Закрыть</Dialog.Close>
      </Dialog.Content>
    </Dialog>
  );
}

Стилизация компонентов Radix UI

Radix UI не навязывает стили, поэтому необходимо самостоятельно оформлять компоненты. В Next.js популярны следующие подходы:

  1. Tailwind CSS Использование утилитарных классов позволяет быстро оформлять компоненты без создания отдельного CSS. Компоненты Radix UI идеально сочетаются с Tailwind благодаря headless-подходу:
<Dialog.Content className="bg-white p-6 rounded shadow-lg">
  <Dialog.Title className="text-xl font-bold">Заголовок</Dialog.Title>
  <Dialog.Description className="text-gray-600">Описание диалога</Dialog.Description>
</Dialog.Content>
  1. Stitches Stitches позволяет создавать стилизованные компоненты с CSS-in-JS и имеет тесную интеграцию с Radix UI:
import { styled } from '@stitches/react';
import * as Dialog from '@radix-ui/react-dialog';

const StyledContent = styled(Dialog.Content, {
  backgroundColor: 'white',
  padding: '2rem',
  borderRadius: '8px',
  boxShadow: '0 10px 25px rgba(0,0,0,0.2)',
});

export default function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Открыть</Dialog.Trigger>
      <StyledContent>
        <Dialog.Title>Заголовок</Dialog.Title>
      </StyledContent>
    </Dialog.Root>
  );
}

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

Radix UI предлагает контролируемые и неконтролируемые компоненты. В Next.js лучше использовать контролируемый режим для обеспечения предсказуемого поведения при SSR.

Пример контролируемого селекта:

import * as Sel ect from '@radix-ui/react-select';
import { useState } fr om 'react';

export default function ControlledSelect() {
  const [value, setValue] = useState('option1');

  return (
    <Sel ect.Root value={value} onValueCha nge={setValue}>
      <Sel ect.Trigger>{value}</Sel ect.Trigger>
      <Select.Content>
        <Select.Item value="option1">Опция 1</Select.Item>
        <Select.Item value="option2">Опция 2</Select.Item>
      </Select.Content>
    </Select.Root>
  );
}

Адаптация под маршрутизацию Next.js

Компоненты Radix UI часто используют ссылки и действия, которые могут взаимодействовать с маршрутизацией. Для корректной работы с Next.js необходимо:

  • Использовать <Link> из next/link для переходов.
  • Избегать прямого изменения window.location без проверки SSR.
  • При использовании модальных окон, тултипов или дропдаунов, сохранять состояние при переходах между страницами с помощью React Context или Zustand.

Пример интеграции Radix Dialog с маршрутизацией:

import Link fr om 'next/link';
import * as Dialog fr om '@radix-ui/react-dialog';

export default function NavigationDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Меню</Dialog.Trigger>
      <Dialog.Content>
        <Link href="/about">О нас</Link>
        <Link href="/contact">Контакты</Link>
        <Dialog.Close>Закрыть</Dialog.Close>
      </Dialog.Content>
    </Dialog.Root>
  );
}

Доступность и фокусировка

Radix UI автоматически управляет фокусом, ролями и aria-атрибутами, что критично для доступности. В Next.js необходимо учитывать:

  • Корректную гидратацию, чтобы не ломались aria-атрибуты.
  • Контролируемое состояние для модальных компонентов, чтобы избежать “потерянного фокуса” при SSR.
  • Управление таб-порядком в сложных интерфейсах с несколькими слоями компонентов.

Оптимизация производительности

Next.js позволяет использовать динамический импорт для компонентов Radix UI, что снижает нагрузку на клиент:

import dynamic fr om 'next/dynamic';

const DynamicDialog = dynamic(() => import('./DialogComponent'), { ssr: false });

export default function Page() {
  return <DynamicDialog />;
}

Это особенно полезно для компонентов, требующих интерактивности, таких как модальные окна, аккордеоны, меню и тултипы.


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