Getting Started
VhyxUI works with any React 18 project. No configuration required. Import tokens, wrap with provider, use components.
Requirements
Node.js≥ 18
React≥ 18
react-dom≥ 18
Installation
Install both packages. Tokens are required — they define every visual decision the components make.
npm install @vhyxui/react @vhyxui/tokens@vhyxui/reactAll 22 components, VhyxUIProvider, and the toast() API.
@vhyxui/tokensPure CSS. Zero JavaScript. Every visual decision as a CSS custom property. Import once — override anything.
Peer dependencies
Provider Setup
Wrap your app with VhyxUIProvider and import the token CSS. Do this once at the root of your application.
App Router
app/layout.tsx
import '@vhyxui/tokens/index.css';
import { VhyxUIProvider } from '@vhyxui/react';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<VhyxUIProvider>
{children}
</VhyxUIProvider>
</body>
</html>
);
}Pages Router
pages/_app.tsx
import '@vhyxui/tokens/index.css';
import type { AppProps } from 'next/app';
import { VhyxUIProvider } from '@vhyxui/react';
export default function App({ Component, pageProps }: AppProps) {
return (
<VhyxUIProvider>
<Component {...pageProps} />
</VhyxUIProvider>
);
}First Component
Import any component from @vhyxui/react. Every component is tree-shakeable — only what you import ships.
import { Button } from '@vhyxui/react';
function MyComponent() {
return (
<div>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
</div>
);
}Complete Example
A login form using react-hook-form and zod for validation. Field handles label association, error display, and accessibility automatically.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button, Input, Form, Field } from '@vhyxui/react';
const schema = z.object({
email: z.string().email('Enter a valid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type LoginFormData = z.infer<typeof schema>;
export function LoginForm() {
const form = useForm<LoginFormData>({
resolver: zodResolver(schema),
});
async function onSubmit(data: LoginFormData) {
// Replace with your auth logic — e.g. signIn from next-auth
await signIn(data);
}
return (
<Form form={form} onSubmit={form.handleSubmit(onSubmit)}>
<Field name="email" label="Email address" required>
<Input
{...form.register('email')}
type="email"
placeholder="you@example.com"
error={!!form.formState.errors.email}
/>
</Field>
<Field name="password" label="Password" required>
<Input
{...form.register('password')}
type="password"
placeholder="••••••••"
error={!!form.formState.errors.password}
/>
</Field>
<Button
type="submit"
variant="primary"
size="md"
loading={form.formState.isSubmitting}
>
Sign in
</Button>
</Form>
);
}