Build full-stack web apps in pure Python. No JavaScript. No HTML templates.
Write your code and business logic in Python. It runs on the server. The browser is just a thin client rendering standard web components: you never touch the frontend.
Under the hood, PyXFlow uses the same server-driven architecture as Vaadin Flow but entirely written in Python, and ships with 50+ Vaadin web components: enterprise-grade, accessible, and battle-tested over years.
from pyxflow import Route
from pyxflow.components import *
@Route("hello")
class HelloView(HorizontalLayout):
def __init__(self):
name = TextField("Your name")
button = Button("Say hello", lambda e: Notification.show(f"Hello {name.value}"))
self.add(name, button)- Server-side only -- all logic runs in Python, no JavaScript to write
- Real-time push -- update the UI from background tasks via WebSocket
- Lazy data loading -- Grid with virtual scrolling and server-side sorting
- Form validation -- Binder with validators and converters
- Theming -- Lumo and Aura themes with dark mode support
- Hot reload --
--devmode watches for file changes
The included demo app showcases real-world patterns across 8 views:
| View | Route | What it shows |
|---|---|---|
| About | / |
Landing page with logo and architecture diagram |
| Hello | /hello |
Simplest view -- TextField + Button + Notification |
| Components | /components |
20+ components in a responsive CSS grid |
| Grid | /grid |
Lazy data provider with sorting and selection (200 rows) |
| Master-Detail | /master-detail |
Grid + side form with Binder validation |
| Responsive CRUD | /crud |
Full CRUD with route parameters and FAB |
| Stopwatch | /push-demo |
WebSocket push -- server updates UI every second |
| File Explorer | /file-explorer |
TreeGrid + @ClientCallable for server method calls |
git clone https://github.com/manolo/pyxflow.git
cd pyxflow
python -m venv .venv && source .venv/bin/activate
pip install -e .
python -m demo # http://localhost:8088pip install pyxflowmkdir my-app && cd my-app
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install pyxflow
pyxflow --setup
pyxflow
# http://localhost:8080This scaffolds a project with views/, static/, and VSCode config:
my-app/
views/
main_layout.py # AppLayout with SideNav
hello_world.py # Sample view with TextField + Button
static/
favicon.ico
styles/
styles.css
images/
.vscode/ # VSCode settings, snippets, extensions
pyxflow [app_module] [options]
app_module Python module with views/ (auto-detected if omitted)
--setup [app_name] Scaffold a new project (views, static, .vscode/)
--vscode Generate .vscode/ config and install recommended extensions
--dev Auto-reload on source changes
--debug Verbose UIDL protocol logging
--port PORT Server port (default: 8080)
--host HOST Server host (default: localhost)
--bundle Generate frontend bundleEach view is a Python class with a @Route decorator that defines the URL path. Add @Menu to include it in the navigation. PyXFlow handles client-side URL routing automatically.
from pyxflow import Route, Menu
@Route("dashboard", page_title="Dashboard", layout=MainLayout)
@Menu(title="Dashboard", order=1, icon="vaadin:dashboard")
class DashboardView(VerticalLayout):
...@Route(path)-- register a view at a URL path@Route(path, layout=MainLayout)-- wrap in a layout@Menu(title, order, icon)-- add to the navigation menu- Route parameters:
@Route("greet/:name")withon_parameter_changed(self, params)
@AppShell marks the global app configuration: push, theme, stylesheets. Only one per app. In practice, you put it on your main layout class (AppLayout) which provides the navbar, drawer, and content area.
from pyxflow import AppShell, Push, ColorScheme, StyleSheet
from pyxflow.components import AppLayout, DrawerToggle, H1, SideNav, SideNavItem
from pyxflow.menu import get_menu_entries
@AppShell
@Push
@ColorScheme("dark")
@StyleSheet("lumo/lumo.css", "styles/styles.css")
class MainLayout(AppLayout):
def __init__(self):
self.add_to_navbar(DrawerToggle(), H1("My App"))
nav = SideNav()
for entry in get_menu_entries():
nav.add_item(SideNavItem(entry.title, entry.path))
self.add_to_drawer(nav)Grid fetches only the rows visible in the viewport. Provide a callback that returns a slice of your data and the total count -- Grid handles virtual scrolling, sorting, and pagination.
grid = Grid()
grid.add_column("name", header="Name").set_sortable(True).set_auto_width(True)
grid.add_column("email", header="Email").set_auto_width(True)
grid.set_data_provider(my_fetch)
def my_fetch(offset, limit, sort_orders):
return items[offset:offset+limit], len(items)Update the UI from business background tasks running in the server. The server pushes changes to the browser via WebSocket. Just call ui.access() from any async task.
import asyncio
@Route("live")
class LiveView(VerticalLayout):
def __init__(self):
self.label = Span("0")
self.add(self.label)
asyncio.get_event_loop().create_task(self._tick())
async def _tick(self):
n = 0
while True:
await asyncio.sleep(1)
n += 1
ui = self.get_ui()
if ui:
ui.access(lambda: self.label.set_text(str(n)))Requires @Push on the @AppShell class.
| Type | Component | Description |
|---|---|---|
| Input | TextField |
Single-line text input |
| Input | TextArea |
Multi-line text input |
| Input | PasswordField |
Masked password input |
| Input | EmailField |
Email input with RFC 5322 validation |
| Input | NumberField |
Numeric input with min/max/step |
| Input | IntegerField |
Integer-only variant |
| Input | Checkbox / CheckboxGroup |
Toggle and multiple selection |
| Input | RadioButtonGroup |
Single selection from options |
| Input | Select |
Dropdown single selection |
| Input | ComboBox / MultiSelectComboBox |
Filterable dropdown (multi-select) |
| Input | DatePicker / TimePicker / DateTimePicker |
Date and time selection |
| Input | Upload |
File upload |
| Input | CustomField |
Composite custom field |
| Data | Grid |
Data table with sorting, selection, lazy loading, renderers |
| Data | TreeGrid |
Hierarchical data table with expand/collapse |
| Layout | VerticalLayout / HorizontalLayout |
Stack children vertically or horizontally |
| Layout | FlexLayout |
CSS Flexbox layout |
| Layout | FormLayout |
Responsive form with labels and colspan |
| Layout | SplitLayout |
Resizable two-panel split |
| Layout | AppLayout |
Application shell with navbar and drawer |
| Layout | Scroller |
Scrollable container |
| Layout | Card |
Styled card container |
| Layout | Details / Accordion |
Collapsible sections |
| Layout | Dialog / ConfirmDialog |
Modal dialogs |
| Layout | MasterDetailLayout |
Master-detail pattern |
| Navigation | Tabs / TabSheet |
Tab navigation with content panels |
| Navigation | SideNav |
Side navigation with items |
| Navigation | MenuBar / ContextMenu |
Hierarchical and right-click menus |
| Navigation | RouterLink |
Client-side navigation link |
| Display | Button |
Click listener, icon support, keyboard shortcuts |
| Display | Icon |
Vaadin and Lumo icon sets |
| Display | Notification |
Toast notifications with position and duration |
| Display | ProgressBar |
Determinate and indeterminate progress |
| Display | Avatar / AvatarGroup |
User avatars |
| Display | Span, H1-H6, Paragraph, Pre |
Text elements |
| Display | Image, Anchor, IFrame |
Media and links |
PyXFlow ships an MCP server that gives AI coding assistants (Claude Code, Cursor, Windsurf, etc.) up-to-date knowledge of every component, pattern, and API. When enabled, your AI can look up constructors, method signatures, theme variants, and working examples without hallucinating outdated information.
Add the PyXFlow MCP server to your editor's config:
Claude Code -- run once in your project:
claude mcp add pyxflow --transport http https://pyxflow-mcp.manolo-345.workers.dev/mcpCursor / Windsurf / Other -- add to your MCP settings file (.cursor/mcp.json, etc.):
{
"mcpServers": {
"pyxflow": {
"type": "http",
"url": "https://pyxflow-mcp.manolo-345.workers.dev/mcp"
}
}
}| Tool | What it does |
|---|---|
get_pyxflow_primer |
Framework overview, imports, component categories |
list_components |
All 78+ components with descriptions |
get_component_api |
Constructor, methods, properties, theme variants for any component |
get_example |
Runnable examples: hello, grid, crud, master-detail, push, dialog, app-layout |
get_pattern |
Pattern guides: routing, binder, data-provider, push, renderers, theming, etc. |
get_constants |
Enum values: layout, grid, field, variants |
The AI calls these tools automatically when you ask it to build PyXFlow views. No manual invocation needed.
See README-DEV.md for setup, tests, project structure, and architecture.
Apache License 2.0




