ValyroUI English version

Начало работы: Waku

React Server Components: компоненты без функций в пропсах рендерятся прямо в серверных компонентах, интерактив — в файлах с 'use client'

1. Создайте приложение

Проверено с Waku 1.0.0-rc.3 (последний релиз — RC, стабильной 1.0 пока нет).

npm create waku@latest -- --project-name my-app
cd my-app

2. Установите пакет

Подходят react и react-dom 17, 18 и 19 (на React 17 id из useId появляются сразу после монтирования). Пакет — ES-модули (плюс CommonJS-копия для require()), без runtime-зависимостей, по файлу на модуль: в бандл попадают только импортированные компоненты, а как грузить и CSS только их — в разделе «Только нужный CSS» ниже. Шрифты — отдельные файлы: тема vercel (по умолчанию в ThemeProvider) использует Geist, поэтому рядом со стилями импортируйте valyro-ui/fonts/geist.css и valyro-ui/fonts/geist-mono.css, иначе текст будет системным шрифтом; glass использует системные шрифты, ей файлы не нужны. Пока пакет не опубликован, соберите и упакуйте его в репозитории (npm run build:core && npm pack ./dist/core) и установите tarball из корня репозитория: npm install /path/to/ValyroUI/valyro-ui-0.1.0.tgz.

npm install valyro-ui

3. Тема на <html>

Без _root.tsx Waku генерирует <html> сам и атрибутов темы в серверном HTML не будет. Создайте корневой элемент — первая отрисовка сразу в теме.

import type { ReactNode } from 'react';

export default async function RootElement({ children }: { children: ReactNode }) {
  return (
    <html lang="en" data-theme="glass" data-mode="light">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </head>
      <body>{children}</body>
    </html>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};

4. Стили и ThemeProvider в layout

Layout — серверный компонент: ThemeProvider получает только строки, поэтому импортируется прямо здесь. Waku сам выводит CSS в <head>. Из шаблонного layout уберите обёртку со шрифтом Nunito (перебивает шрифт темы) и фиксированные Header/Footer; Tailwind из styles.css можно оставить — его preflight лежит в @layer base и стилям ValyroUI не мешает. При SSR задайте locale в ThemeProvider: иначе числа и даты форматируются по локали сервера, а в браузере — по локали пользователя, и гидрация падает на несовпадении текста («1,200» против «1 200»).

import '../styles.css';
import 'valyro-ui/styles.css';

import type { ReactNode } from 'react';
import { ThemeProvider } from 'valyro-ui';

type RootLayoutProps = { children: ReactNode };

export default async function RootLayout({ children }: RootLayoutProps) {
  return (
    <ThemeProvider theme="glass" mode="light" locale="en-US">
      {children}
    </ThemeProvider>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};

5. Страница — серверный компонент

Пакет помечен 'use client', поэтому Card, Badge и другие компоненты с сериализуемыми пропсами рендерятся прямо из серверного компонента. Всё с обработчиками — в файле с 'use client': Showcase из гайда по Next.js лежит в src/components/Showcase.tsx без изменений. Функция в пропсе из серверного компонента (<Button onClick={…}>) роняет сборку: «Event handlers cannot be passed to Client Component props».

import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from 'valyro-ui';
import { Showcase } from '../components/Showcase';

// Server Component: ValyroUI components with serializable props render here directly;
// anything with handlers or state lives in the 'use client' Showcase.
export default async function HomePage() {
  return (
    <>
      <title>ValyroUI on Waku</title>
      <div style={{ padding: '32px 32px 0', maxWidth: 720 }}>
        <Card>
          <CardHeader>
            <CardTitle>Server Component</CardTitle>
            <CardDescription>Rendered on the server</CardDescription>
          </CardHeader>
          <CardContent>
            <Badge color="green" dot>
              RSC
            </Badge>
          </CardContent>
        </Card>
      </div>
      <Showcase />
    </>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};

6. Соберите и запустите

npx tsc --noEmit
npm run build
npm run start

Другие фреймворки