fabric ·nu.ui

The browser surface for Nu apps.

Write your widgets in Python. Nu draws them in the browser. Clicks and edits come back to you as writes on the same widget.

Powered byReactZustand

See it.

One class per page. One attribute per widget. Set a value, the browser shows it.

Every widget is a Ref you hang off a class. StatRef, TableRef, ButtonRef, InputRef, ChartRef, and about thirty more. Subclass Row, Card, or Page to compose your own layouts.

Writes from Python land in the browser. Clicks and edits in the browser come back to Python through the same Ref. One wire protocol handled for you, async only.

See: a small dashboard with a stats row, a table, and a refresh button. One Python file, no JavaScript.

dashboard.py
python · 29 locpy
import nu
class StatsRow(nu.ui.Row):
users = nu.ui.StatRef.slot()
online = nu.ui.StatRef.slot()
health = nu.ui.BadgeRef.slot()
class Dashboard(nu.ui.Page):
stats = StatsRow.slot()
table = nu.ui.TableRef.slot(columns=["user", "last seen"])
refresh = nu.ui.ButtonRef.slot(label="Refresh")
class App(nu.ui.Index):
pages = nu.ui.Pages({"/": Dashboard})
# writes from python go straight to the browser
paint = (
Dashboard.stats.users.set_value("142")
| Dashboard.stats.online.set_value("38")
| Dashboard.stats.health.set(label="healthy", variant="ok")
| Dashboard.table.set(rows=[["ada", "2m ago"], ["bob", "just now"]])
)
# clicks come back into the same ref
on_refresh = nu.ReactForever(Dashboard.refresh.clicked(), paint)
# serve it
app = nu.With(nu.ui.server(paint >> on_refresh), body=nu.ForeverDo(nu.Delay(3600)))
nu.run(app)

What your program gains.

Four properties once your surface lives on nu.ui.

one language full stack

Same tree on both sides of the wire.

Server-side Python declares the widgets. The client renders them. One vocabulary top to bottom, no template DSL, no client codegen.

component kit built in

About thirty widgets, sensible defaults.

Inputs, outputs, layouts, charts. ButtonRef, TableRef, InputRef, ChartRef, Card, Column, Row. Compose your own by subclassing Section.

reactive by default

Bind once, state and ui stay in sync.

Any attribute emits on change. Wire a state attribute to a widget attribute and both directions flow through the same hook. No polling, no manual invalidation.

no client build step

No npm, no bundler, no compile.

The Python process serves the page. You never open a package.json. Just python app.py.

Try Nu.

One command gets you the wheel with every fabric. Then follow the movies tutorial to build a real app in an afternoon.

01 Install

pip install "nustack-py[all]"

02 Run the demo

nu demo movies

03 Build your app

Browse examples

Like what you see?

The project is young. Star it, join the room, watch what we ship next.