That screenshot is the real component, running as WebAssembly — clicking a
row runs your update. The other half of the file is Burxt: a Model, a
Msg, and one update function, which
chapter 7 walks through.
Twenty-one of these, all running
Every example in the repository, captured in a browser beside the markup that made it.
Drag the strip, or pick one.
1 / 16
Todos.sbmxa list, a text field, and state carried as text
Notes.sbmx`Store`, `Load` and `Focus` — the browser's own storage, and a caret
:props: model: Model
:!props:
:div: class=bx-card
:div: class=bx-spread
:h2: class=bx-title child={"Draft"} :!h2:
:span: class="bx-badge bx-badge-quiet" child={model.status} :!span:
:!div:
:hr: class=bx-divider :!hr:
:textarea: class="bx-area draft" on:input=Msg.Typed(value)
{{ model.text }}
:!textarea:
:div: class=bar
:button: class="bx-btn bx-btn-primary" child=save on:click=Msg.Save :!button:
:button: class=bx-btn child=restore on:click=Msg.Restore :!button:
:button: class="bx-btn bx-btn-quiet" child=clear on:click=Msg.Clear :!button:
:!div:
:p: class=bx-muted
It survives a reload — `Store` and `Load` are the browser's own storage, and `clear` puts the caret
back in the field.
:!p:
:!div:
Clock.sbmxa `pure` view cannot read a clock, so a tick asks the server
:props: model: Model
:!props:
:div: class=bx-card
:div: class=bx-spread
:div:
:h2: class=bx-title child={"Server time"} :!h2:
:p: class=bx-muted child={to_string(model.ticks) + " ticks, each one asked of the server"} :!p:
:!div:
:button: class="bx-btn bx-btn-primary" child={button_text(model)} on:click=Msg.Toggle :!button:
:!div:
:hr: class=bx-divider :!hr:
:p: class=face child={label(model)} :!p:
:p: class=bx-muted
A `pure` view cannot read a clock, and a tick carries no payload — so the time comes from where time
lives.
:!p:
:!div:
Snake.sbmxa game: a tick, four global keys, a list that moves every frame
:props: model: Model
:!props:
:div: class=bx-card
:div: class=bx-spread
:div:
:h2: class=bx-title child={"Snake"} :!h2:
:p: class=bx-muted child={"Arrow keys. A tick, four global keys, and a list that moves every frame."} :!p:
:!div:
:span: class=bx-badge child={verdict(model)} :!span:
:!div:
:hr: class=bx-divider :!hr:
:div: class=board
:for: seg in model.body key to_string(seg.x) + "-" + to_string(seg.y)
:div: class=seg style={{ at(seg) }} :!div:
:!for:
:div: class=food style={{ at(model.food) }} :!div:
:!div:
:div: class=bx-row
:button: class="bx-btn bx-btn-primary" child={"new game"} on:click=Msg.Again :!button:
:!div:
:!div:
Cart.sbmxmoney: `Decimal<2>`, a percent literal, and `money_split` placing the odd penny
For anything past a counter, a component has two halves — the markup, and a ===bx section holding
your own Burxt:
===bx
class Item { id: Int, name: String }
class Model { count: Int, items: [Item] }
enum Msg { Increment, Reset }
pure function update(msg: Msg, m: Model) -> Model {
match msg {
Increment => { return Model { count: m.count + 1, items: m.items }; }
Reset => { return Model { count: 0, items: m.items }; }
}
}
===
:props: model: Model
:!props:
# Counter
The count is {{ to_string(model.count) }}.
:button: on:click=Msg.Increment
increment
:!button:
on:click=Msg.Increment names a message, and your update decides what it does. That’s one
function you can read, test, and diff — not a closure that re-runs.
What a component can do
You write
You get
# Heading and paragraphs
headings and paragraphs, as in any Markdown
{{ total }}
your data, in the page
:props: model: Model
what the component is given
:button: on:click=Msg.Add
a button, and your update decides what it does
:for: item in items key item.id
a row per item, each with its own buttons
:match: model.route
a screen per route — forget one and the build fails
:if: ready / :else:
a section that appears when it should
:Badge: amount={{ n }}
another component, imported with use
:span: child={total} :!span:
a leaf on one line, body and all
===style.local
CSS scoped to this component, with no default
commands and watch
fetching, timers, websockets — without await
load
server rendering, and server code that cannot reach the browser
That is the whole idea. If you can write a document, you can write an app.
Six short chapters. By the end you will have built a counter, a form, a list, and a price table, and
put them on a real page.
The one thing that will surprise you
star-burxt reads your buttons.
Most frameworks treat what happens on a click as your business — it is a function, it runs, and if
it is wrong you find out when a user finds out. Here, a click is checked before the page exists.