Setup is the same everywhere: install the package, mount AppProvider, import
the token layer. The one part that differs by host is teaching the bundler that
@hanzo/gui is authored against React Native primitives.
There is no tailwind.config.js to write, no PostCSS plugin to register, and no
CLI that copies component source into your repository.
Install
npm install @luxfi/ui @hanzo/gui @tanstack/react-queryreact and react-dom are peers at >=19. @tanstack/react-query is a peer
rather than a dependency on purpose: it hands out a React context, and a second
physical copy is a second context — AppProvider would publish a client into
one while useQuery read the other.
Mount the provider
import { AppProvider } from '@luxfi/ui/provider'
import '@luxfi/ui/tokens.css'
export default function App() {
return <AppProvider>{/* your app */}</AppProvider>
}AppProvider is GuiProvider fed the Lux config, plus a
QueryClientProvider. Pass your own client as queryClient if the app already
has one.
Server-rendering, pass the request's host so the first paint is already the right brand:
<AppProvider host={req.headers.host}>{children}</AppProvider>Omit it in the browser and the host is read from location. Never bake a brand
into a deployment — see White-label.
Add the fonts and the component sheet
import '@hanzo/font/css'
import '@hanzo/ui/styles.css'
import '@hanzo/ui/theme.css'
import '@luxfi/ui/tokens.css'@hanzo/font/css matters beyond the face. @hanzo/design publishes
--text-*: calc(<rem> * var(--type-scale, 1)), gui's $n ladder resolves
through those custom properties, and @hanzo/appearance writes --type-scale
onto <html>. Without the import the var() falls back to its frozen px
literal and a reader's type-size setting reaches nothing.
Wire the bundler
The engine's dependency graph names react-native, and one of those packages —
react-native-svg, under the icon set — ships a native entry beside a .web.js
sibling. Picking the native one on a web build is a parse failure in Flow source
inside a dependency, which reads as a broken package rather than a missing
setting.
One plugin under Vite, one wrapper under Next. For any other bundler, see Manual for the settings both of them apply.
Render something
import { Button } from '@luxfi/ui/button'
import { XStack } from '@luxfi/ui/gui'
export default function Home() {
return (
<XStack gap={8} padding={24}>
<Button>Click me</Button>
</XStack>
)
}Component subpaths are one per component: @luxfi/ui/button,
@luxfi/ui/accordion, @luxfi/ui/drawer. The engine is at @luxfi/ui/gui,
separately, because the skin and the engine both define Button, Input,
Image, Switch, Separator, Select, Tabs, Avatar, Checkbox,
Slider and Progress.
The export map is frozen: ., ./tokens.css, the component subpaths, and
/provider /utils /gui /white-label /vite /bank /explore
/explore/*. Deep-subpath consumers are patched forward, never re-majored.
TypeScript
The package is TypeScript and ships its own declarations. The Lux config
augments the engine's types, so <Theme name="lux"> and fontSize="$3" are
checked rather than stringly-typed:
declare module '@hanzo/gui' {
interface GuiCustomConfig extends Conf {}
}That augmentation ships with @luxfi/ui. You do not repeat it.