Skip to main contentSkip to navigation

Installation

Install @luxfi/ui, mount the provider, wire the engine into your bundler.

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-query

react 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.