Начало работы: 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>
);
}