ValyroUI Русская версия

Getting started: Next.js

The package is marked 'use client', so you can import components straight into Server Components; the theme is set on <html> on the server, with no flash

1. Create the app

npx create-next-app@latest my-app --ts --app --no-tailwind
cd my-app

2. Install the package

Works with react and react-dom 17, 18 and 19 (React 17: ids from useId appear right after mount). The package ships ES modules (plus a CommonJS copy for require()), has no runtime dependencies and has one file per module, so your bundle includes only the components you import; to load only their CSS as well, see “Only the CSS you need” below. Fonts are separate files: the vercel theme (the ThemeProvider default) uses Geist, so import valyro-ui/fonts/geist.css and valyro-ui/fonts/geist-mono.css next to the styles, or text falls back to the system font; glass uses system fonts and needs none. Until the package is published, build and pack it in the repository (npm run build:core && npm pack ./dist/core) and install the tarball from the repository root: npm install /path/to/ValyroUI/valyro-ui-0.1.0.tgz.

npm install valyro-ui

3. Flash-free theme

The data-theme and data-mode attributes go straight into the server-rendered <html>, so the first paint already uses the right theme; ThemeProvider (a client component) keeps them in sync after hydration. The values must match in both places — if the theme is stored in a cookie, read it in the layout and pass it to both. Delete the template's globals.css or remove the body background from it. With SSR, pass locale to ThemeProvider. Otherwise numbers and dates are formatted in the server locale on the server and in the user locale in the browser, and hydration fails on the mismatched text (“1,200” vs “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. Interactivity in a client component

Functions can't be passed from a Server Component, so anything with handlers (onClick, onValueChange, toast()) lives in a 'use client' file. The package's utilities (toast, formatNumber, etc.) are client-side too: call them only from client code.

'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. The page as a Server Component

Components with only serializable props (Card, Badge, icons) can render directly in a 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. Build and run

npx next build
npx next start

7. Pages Router

Import the package's global CSS only in pages/_app.tsx, and put the theme attributes in pages/_document.tsx. Pages use the same 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: theme on the server

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>
  );
}

Other frameworks