ValyroUI English version

Начало работы: Next.js

Пакет помечен 'use client': компоненты импортируются прямо из Server Components, тема ставится на <html> на сервере — без вспышки

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

npx create-next-app@latest my-app --ts --app --no-tailwind
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. Тема без вспышки

Атрибуты data-theme и data-mode пишутся прямо в серверную разметку <html>, поэтому первая отрисовка уже в нужной теме; ThemeProvider (клиентский компонент) держит их в синхроне после гидрации. Значения в обоих местах должны совпадать — если тема хранится в cookie, прочитайте её в layout и передайте в оба. Шаблонный globals.css удалите или уберите из него фон body. При SSR задайте locale в ThemeProvider: иначе числа и даты форматируются по локали сервера, а в браузере — по локали пользователя, и гидрация падает на несовпадении текста («1,200» против «1 200»).

import type { Metadata } from 'next';
import { ThemeProvider } from 'valyro-ui';
import 'valyro-ui/styles.css';

export const metadata: Metadata = { title: 'ValyroUI on Next.js' };

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    // Set the theme on <html> in the server markup so the first paint is already themed.
    <html lang="en" data-theme="glass" data-mode="light">
      <body>
        <ThemeProvider theme="glass" mode="light" locale="en-US">
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

4. Интерактив — в клиентском компоненте

Функции нельзя передать из Server Component, поэтому всё с обработчиками (onClick, onValueChange, toast()) живёт в файле с 'use client'. Утилиты пакета (toast, formatNumber и т. п.) — тоже клиентские: вызывайте их только из клиентского кода.

'use client';
import { useState } from 'react';
import { BarChart, Button, DataTable, Dialog, Select, Toaster, toast, type DataTableColumn } from 'valyro-ui';

interface Invoice {
  id: string;
  customer: string;
  amount: number;
}

const invoices: Invoice[] = [
  { id: 'INV-001', customer: 'Acme', amount: 1200 },
  { id: 'INV-002', customer: 'Globex', amount: 860 },
  { id: 'INV-003', customer: 'Initech', amount: 2310 },
];

const columns: DataTableColumn<Invoice>[] = [
  { key: 'id', header: 'Invoice', sortable: true },
  { key: 'customer', header: 'Customer', sortable: true },
  { key: 'amount', header: 'Amount', sortable: true, align: 'right' },
];

const revenue = [
  { month: 'Jan', revenue: 120 },
  { month: 'Feb', revenue: 180 },
  { month: 'Mar', revenue: 150 },
];

export function Showcase() {
  const [region, setRegion] = useState('eu');
  const [open, setOpen] = useState(false);
  return (
    <main style={{ display: 'grid', gap: 24, padding: 32, maxWidth: 720 }}>
      <div style={{ display: 'flex', gap: 12 }}>
        <Button onClick={() => toast.success('Saved')}>Show toast</Button>
        <Button variant="secondary" onClick={() => setOpen(true)}>
          Open dialog
        </Button>
        <Select
          aria-label="Region"
          value={region}
          onValueChange={setRegion}
          options={[
            { value: 'eu', label: 'Europe' },
            { value: 'us', label: 'United States' },
          ]}
        />
      </div>
      <DataTable label="Invoices" data={invoices} columns={columns} getRowId={(row) => row.id} />
      <BarChart ariaLabel="Revenue per month" data={revenue} x="month" series={[{ key: 'revenue', label: 'Revenue' }]} />
      <Dialog
        open={open}
        onOpenChange={setOpen}
        title="Deploy to production"
        footer={<Button onClick={() => setOpen(false)}>Done</Button>}
      >
        Ready when you are.
      </Dialog>
      <Toaster />
    </main>
  );
}

5. Страница — Server Component

Компоненты только с сериализуемыми пропсами (Card, Badge, иконки) можно рендерить прямо в Server Component.

import { Badge, Card } from 'valyro-ui';
import { Showcase } from './Showcase';

// A Server Component: ValyroUI components without function props render here directly.
export default function Page() {
  return (
    <>
      <Card style={{ margin: 32 }}>
        <Badge color="green">Server-rendered</Badge>
      </Card>
      <Showcase />
    </>
  );
}

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

npx next build
npx next start

7. Pages Router

Глобальный CSS из пакета импортируется только в pages/_app.tsx; атрибуты темы — в pages/_document.tsx. Страницы используют тот же Showcase.

import type { AppProps } from 'next/app';
import { ThemeProvider } from 'valyro-ui';
import 'valyro-ui/styles.css';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider theme="glass" mode="light" locale="en-US">
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

8. Pages Router: тема на сервере

import { Head, Html, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en" data-theme="glass" data-mode="light">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

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