Build A Real Portal¶
This tutorial is for engineers and evaluation teams who want to understand how BrickflowUI feels when used to build a serious internal product, not just a toy demo.
By the end, you should be able to design a multi-page portal with:
- responsive navigation
- controlled inputs and filters
- tables, charts, and pipeline views
- media surfaces
- auth-aware page structure
- theme-driven branding
- custom routes and form actions
- Databricks-ready deployment structure
The Target¶
We are going to think like a SaaS team building a platform portal. The finished app should feel like one product with several jobs:
- an executive dashboard for high-level signals
- an operations view for tables, exports, and drilldowns
- a pipeline view for DAG-style runtime visibility
- an assistant workspace for guided investigation
- a secure admin surface for privileged actions
That is the right mental model for BrickflowUI. It is not only a dashboard library. It is a Python-first app framework for structured internal web products.
Step 1: Start With The Shell¶
Create the app, define your theme, and choose your loading experience.
from pathlib import Path
import brickflowui as db
app = db.App(
title="Platform Control Center",
theme={
"branding": {"title": "Platform Control Center"},
"colors": {
"primary": "#0F766E",
"surface": "#FFFFFF",
"background": "#F6FBFA",
"text": "#0F172A",
"text_muted": "#475569",
"border": "#D6E6E4",
},
},
loading={
"title": "Platform Control Center",
"message": "Preparing the portal runtime...",
"animation": "pulse",
},
)
Why this matters:
App(...)is where the global product frame startsthemecontrols the long-lived brand languageloadingnow controls the first impression before the websocket connects
Step 2: Model The Pages¶
Think in routes, not giant monolith functions.
@app.page("/", title="Overview", icon="LayoutDashboard")
def overview():
...
@app.page("/pipeline", title="Pipeline", icon="GitBranch")
def pipeline():
...
@app.page("/assistant", title="Assistant", icon="Sparkles")
def assistant():
...
If you register more than one page, BrickflowUI automatically wraps your content with the app shell and sidebar.
Step 3: Build Reusable View Helpers¶
Do not let page functions become one thousand lines long. Create helper functions for repeated surfaces.
def metric_card(label: str, value: str, delta: str, delta_type: str) -> db.VNode:
return db.Card(
[
db.Stat(label=label, value=value, delta=delta, delta_type=delta_type),
],
hover=True,
animated=True,
)
This is one of the biggest skill shifts with BrickflowUI: treat your UI as a composed Python tree.
Step 4: Add Controlled Filters¶
Controlled inputs are how serious portals stay predictable.
@app.page("/", title="Overview")
def overview():
query, set_query = db.use_state("")
team, set_team = db.use_state("platform")
urgent_only, set_urgent_only = db.use_state(False)
return db.Card(
[
db.Input("query", label="Search", value=query, on_change=set_query),
db.Select(
"team",
label="Team",
value=team,
on_change=set_team,
options=[
{"label": "Platform", "value": "platform"},
{"label": "Operations", "value": "operations"},
],
),
db.Checkbox("urgent", "Only urgent", checked=urgent_only, on_change=set_urgent_only),
]
)
What changed recently and why it matters:
- falsy values like
Falseand""now round-trip correctly - users can check and uncheck controls without UI drift
- text inputs can safely become empty strings without getting stuck
Step 5: Add High-Signal Surfaces¶
A good portal gives several levels of detail on one screen.
db.Grid(
[
metric_card("Freshness", "9 min", "-2 min", "decrease"),
metric_card("Success Rate", "98.7%", "+0.4%", "increase"),
metric_card("Cost", "$4.2k", "-3.1%", "decrease"),
],
cols=3,
)
Then layer in:
Tablefor structured detailStatusStripfor compact operational signalsTimelinefor event narrationBadgeandAlertfor focused context
Step 6: Add Charts And Pipeline Views¶
Most enterprise portals need more than one visual grammar.
Recommended pattern:
ComposedChartfor throughput plus SLAHeatmapfor failures across time and domainGaugeChartfor readiness or qualityPipelineGraphfor DAG-like mental modelsPlotwhen you need full Plotly power
db.Grid(
[
db.Card([
db.ComposedChart(
trend_rows,
x_key="week",
bar_keys=["runs"],
line_keys=["success_rate"],
title="Runs vs success rate",
)
]),
db.Card([
db.PipelineGraph(nodes, edges, title="Pipeline topology")
]),
],
cols=2,
)
Step 7: Add Workflow Components¶
Many internal products are only partly dashboards. The rest is workflow.
Use:
Drawerfor detail without leaving contextPopupfor lightweight confirmationsModalfor heavier task flowsKanbanBoardfor issue triageStepperfor release or onboarding stagesChatMessageandChatInputfor assistant surfaces
Example:
detail_open, set_detail_open = db.use_state(False)
db.Button("Open details", on_click=lambda: set_detail_open(True))
db.Drawer(
visible=detail_open,
title="Runtime detail",
on_close=lambda: set_detail_open(False),
children=[
db.Text("Show evidence, logs, sources, or operator guidance here."),
],
)
Step 8: Add Media¶
You can now render images, GIFs, and videos directly from Python.
db.Image("assets/architecture.png", alt="Architecture", caption="Local files are served automatically.")
db.Video(
"assets/demo.mp4",
caption="Product walkthrough",
controls=True,
)
Why this is useful:
- onboarding walkthroughs
- architecture diagrams
- product demos
- clinical or manufacturing process explainers
- richer landing pages
Step 9: Add Auth And Governance¶
For serious internal tools, build page boundaries early.
app = db.App(
auth_mode="user",
auth_provider=db.HeaderAuthProvider(),
allow_anonymous=False,
)
@app.page("/admin", title="Admin", access="user", roles=["admin"])
def admin():
return db.Text("Restricted admin view")
This lets you separate:
- public product pages
- authenticated workspaces
- admin views
- data-governance or security review pages
Step 10: Add Forms And Backend Actions¶
Use Form for simple JSON posting to your own route.
@app.route("/api/action-plan", methods=["POST"])
async def create_action_plan(request):
payload = await request.json()
return {"status": "ok", "summary": payload.get("summary")}
db.Form(
[
db.Input("owner", label="Owner"),
db.Input("summary", label="Summary"),
db.Button("Create plan", html_type="submit"),
],
action="/api/action-plan",
method="POST",
reload_on_success=True,
)
Step 11: Design For Mobile Early¶
The current library is now substantially better on mobile, but good app structure still matters.
Design recommendations:
- avoid overly wide tables above the fold
- let rows wrap when showing badges or action groups
- use drawers for detail instead of stuffing too much into cards
- keep hero sections readable on one column
- prefer 2-column desktop grids that collapse to 1-column naturally
Step 12: Package For Databricks Apps¶
Your app project usually needs:
app.py
requirements.txt
app.yaml
Example:
command:
- python
- app.py
env:
- name: BRICKFLOWUI_ENV
value: production
Recommended Capstone¶
If you want to really learn the library, build one portal with all of these:
- executive overview
- pipeline topology page
- assistant page
- secure admin page
- local media assets
- Plotly plus native charts
- forms posting to custom routes
- auth-aware routing
- theme branding
- responsive review on mobile
That one exercise will teach you far more than isolated snippets.
Best Companion Examples¶
After this tutorial, open these examples:
component_studioclinical_trial_command_centersecure_internal_toolspipeline_observability_015workspace_studio
Each one is opinionated on purpose. The goal is to show how teams actually build portals, not just how to render a button.