Frameworks
One stylesheet import, then components. Each framework below is what the packages actually export.
Next.js
// app/layout.tsx
import "@noorddev/vlak-react/css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>{children}</body>
</html>
);
}
// app/page.tsx: a Server Component. Dialog is "use client" already.
import { Button, Dialog } from "@noorddev/vlak-react";App router. Import the stylesheet once in the root layout. Components that hold state carry "use client" inside the package, so a Server Component can render them directly. Static export works; this site is one:
// next.config.mjs
export default { output: "export", trailingSlash: true };For the dark scheme without a flash, set data-theme in an inline script before hydration and add suppressHydrationWarning on <html>. See Theming. Writing your own StyleX leaves in Next is covered in StyleX.
Vite
// src/main.tsx
import "@noorddev/vlak-react/css";
import { createRoot } from "react-dom/client";
import { App } from "./App";
createRoot(document.getElementById("root")!).render(<App />);Nothing to configure. Vite bundles the CSS import and keeps the package's ESM modules. Add @stylexjs/unplugin only if you write leaves of your own.
Remix and React Router
// app/root.tsx (React Router 7 framework mode, Remix)
import "@noorddev/vlak-react/css";
// or, with the links() convention:
import vlakStyles from "@noorddev/vlak-react/css?url";
export const links = () => [{ rel: "stylesheet", href: vlakStyles }];Side-effect CSS imports work in React Router 7 framework mode and Remix on Vite. Projects on the links() convention use the ?url import instead. Dialog, Sheet, and Drawer render a native <dialog> and open after hydration, so server-rendered markup stays inert until React attaches.
Astro
---
// src/layouts/Base.astro
import "@noorddev/vlak-react/css";
import { Button } from "@noorddev/vlak-react";
import { Dialog } from "@noorddev/vlak-react";
---
<html lang="en">
<body>
<Button>Static, no JavaScript shipped</Button>
<Dialog client:load open={false}>…</Dialog>
<slot />
</body>
</html>Import the stylesheet in a layout. Stateless components (Button, Badge, Card, Table) render to HTML with no client JavaScript. Interactive ones (Dialog, Select, Tabs, the menus) need a client directive; client:load is the simplest.
Plain HTML
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />
</head>
<body>
<div class="rs-field">
<label class="rs-field-label" for="name">Name</label>
<input class="rs-input rs-input-full" id="name" />
</div>
<button class="rs-btn-primary">Save</button>
</body>
</html>Install @noorddev/vlak, or copy vlak.css and the Inter files with npx @noorddev/vlak-cli init. Every component page lists its markup and classes; the behaviour that needs no script (details, dialog, popover, scroll snap) comes from the browser. Set data-theme="dark" on <html> for the dark scheme.
Exports
| Specifier | Contains |
|---|---|
@noorddev/vlak-react | Every component, cx, rs, the icon set, the token vars |
@noorddev/vlak-react/css | The precompiled stylesheet, 42 KB, layered |
@noorddev/vlak-react/components/<name> | One component module |
@noorddev/vlak-react/tokens.stylex | vlak and mq for your own StyleX leaves |
@noorddev/vlak | vlakTokens, the registry, the radius helpers |
@noorddev/vlak/css | vlak.css, 84 KB, every rs-* class |
@noorddev/vlak/css/components.css | Components only, for pages that already load the React stylesheet |
@noorddev/vlak/css/tokens.css | The custom properties alone |
@noorddev/vlak/css/components/<name>.css | One component's CSS |
@noorddev/vlak/tokens | The tokens as JSON |
@noorddev/vlak/props | Every export's props as JSON |