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
src/Label.tsx
src/myTheme.tsx
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 sameawaitio/vite config plus its stylesheet:
vite.config.mts
src/theme.css
- The build writes
dist/ui/ui.jsanddist/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
componentslayer, under the UI’s own utilities, so the page layout always wins over a block’s look. Installtailwindcssand@tailwindcss/vitein 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 fromawaitio/ui as <Block>Props (LabelProps, InputProps, TableProps…), and draw whatever you like:
src/Input.tsx
awaitio/ui keep a block wired to the rest of the UI:
useFieldId()returns the id the surroundingFieldgave 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 aLinkblock on a plain click, and leave clicks with a modifier key to the browser so they still open a new tab.