Skip to content

FluxUI

Build beautiful, reactive Python web UIs — without writing a single line of JavaScript.

FluxUI uses WebSockets to surgically re-render only the components that changed. Define your UI in pure Python, run it like a script.

Why FluxUI?

WebSocket-Reactive

State changes push targeted HTML patches over a persistent WebSocket — no full page reload, no polling. Only the components that actually changed are re-rendered.

🐍

Zero JavaScript

Write your entire UI — logic, layout, and interactivity — in pure Python. No JSX, no bundlers, no node_modules.

🧩

Component Overrides

Replace any built-in component with your own implementation via @ui.override("Button"). Works globally or per-page.

📄

Multi-Page SPA

Declare pages with @app.page("/path"). Navigation happens client-side with no full page reload. Path parameters and programmatic navigation included.

🔌

Plugin Ecosystem

Extend FluxUI with installable Python packages. ui.use_plugin("fluxui-charts") drops in new components and themes with a single line.

🌙

Dark Mode Built-In

Every component is styled with CSS custom properties. Switch themes at runtime with session.set_theme("dark") — no flash, no overhead.


See It in 15 Lines

import fluxui as ui

app = ui.App("Counter")

@app.page("/")
def home(session):
    count = ui.state(0)          # (1) reactive variable
    dark  = ui.state(False)

    async def toggle(v):
        dark.set(v)
        await session.set_theme("dark" if v else "light")

    with ui.Column(gap="var(--s-6)"):
        with ui.Row(justify="space-between"):
            ui.Heading("Counter Demo", level=1)
            ui.Switch("Dark mode", checked=dark, on_change=toggle)

        with ui.Card(title="Click the buttons"):
            with ui.Row(gap="var(--s-3)", align="center"):
                ui.Button("−", variant="secondary",
                          on_click=lambda: count.update(lambda v: v - 1))
                ui.Metric(label="Count", value=lambda: str(count.value))  # (2) auto-subscribed
                ui.Button("+", on_click=lambda: count.update(lambda v: v + 1))
            ui.Button("Reset", variant="ghost", on_click=lambda: count.set(0))

app.run(port=8000)
  1. ui.state(0) creates a reactive variable. Any component that reads it inside render() will automatically re-render when it changes.
  2. Passing a lambda to value= subscribes the Metric to count. When count changes, only this metric is updated — not the whole page.

How FluxUI Compares

Feature FluxUI Streamlit Gradio Panel
Targeted re-renders Yes No No Partial
Multi-page SPA Yes Partial No Yes
Component override Yes No No No
Plugin ecosystem Yes Limited No Limited
Dark mode Runtime Config only Config only Config only
File upload Yes Yes Yes Yes
Charts (Plotly) Yes Yes Yes Yes
DataGrid Yes No No Yes
Custom JS Yes Experimental No Yes
Bundle size ~40 KB ~3 MB ~2 MB ~5 MB

Next Steps