Skip to main content
Everything AwaitIO draws is built from a small set of blocks: Field, Label, Input, Button, Table and the rest. The core only knows their props; a theme gives each block its look. Components never style themselves, so a theme restyles every form, table and page at once.

Use the default theme

app.ts
theme is required and has no default: it says which theme package draws each block. defaultTheme from @awaitio/theme-default draws all of them.

Change one block

Spread a theme and replace the blocks you want with blocks of another theme package. To give every field a different label:
app.ts
Every block is required in the type, so a theme missing one does not compile. Each block knows which package it comes from, so the UI loads exactly the theme scripts the blocks need.
src/Label.tsx
src/myTheme.tsx
The default Field draws its label with Label from awaitio/ui, which is the block the theme picked, so the new label shows in every form: inputs, selects and your own components, see Building blocks. The demo app does exactly this in apps/demo/components/theme.

Package layout

A theme is a package like your own component, built with the same awaitio/vite config plus its stylesheet:
vite.config.mts
src/theme.css
  • The build writes dist/ui/ui.js and dist/ui/ui.css; the server serves them and the UI loads the theme scripts before it draws anything, the login page included.
  • The theme’s classes go into the components layer, under the UI’s own utilities, so the page layout always wins over a block’s look. Install tailwindcss and @tailwindcss/vite in the theme package.
  • A block put under another name (Label: myTheme.Button) or missing from plain JavaScript stops the app at start with a message saying which one.

Blocks

Write a block

A block is a plain React component. Type it with the props the core gives it, exported from awaitio/ui as <Block>Props (LabelProps, InputProps, TableProps…), and draw whatever you like:
src/Input.tsx
Two helpers from awaitio/ui keep a block wired to the rest of the UI:
  • useFieldId() returns the id the surrounding Field gave its control. Put it on the <input>, <textarea> or <select> your block draws: the label points at that id, so clicking the label focuses the field and screen readers read them together. A control block without it still works, but loses its label.
  • navigateTo(href) changes the page of the AwaitIO UI without reloading it. Call it from a Link block on a plain click, and leave clicks with a modifier key to the browser so they still open a new tab.