|

How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat

🚀

In this tutorial, we construct a absolutely interactive, multi-page net software utilizing NiceGUI. We begin by organising the atmosphere and designing a reusable format that features navigation, theming, and darkish mode help. As we transfer ahead, we implement a reside dashboard with real-time metrics and charts, demonstrating reactive bindings and timed updates. We then lengthen the appliance with a full CRUD-based todo system, adopted by a validated kind with dialogs and consumer suggestions mechanisms. We additionally incorporate file add performance with dynamic previews and conclude the characteristic set with an asynchronous chat interface that simulates real-time interplay. Also, we be sure that the app runs seamlessly in Colab by utilizing background threading and dynamic port allocation.

import sys
import subprocess
subprocess.run([sys.executable, "-m", "pip", "install", "-q", "nicegui"], test=True)


import threading, time, random, asyncio, base64, socket
from datetime import datetime
from nicegui import ui, occasions




class State:
   def __init__(self):
       self.todos = [
           {"id": 1, "task": "Explore NiceGUI",      "done": True,  "priority": "High"},
           {"id": 2, "task": "Build a dashboard",    "done": False, "priority": "Medium"},
           {"id": 3, "task": "Deploy to production", "done": False, "priority": "Low"},
       ]
       self.next_id = 4
       self.metrics = {"customers": 1247, "income": 8420, "orders": 53}
       self.sequence = [random.uniform(20, 80) for _ in range(20)]
       self.messages = [{"role": "assistant",
                         "text": "Hi! Type something and I will echo it back."}]


state = State()




def page_shell(lively: str) -> None:
   darkish = ui.dark_mode()
   drawer = ui.left_drawer(worth=True).lessons("bg-grey-2")
   with drawer:
       ui.label("Navigation").lessons("text-lg font-bold p-2")
       for label, path, icon in [
           ("Dashboard", "/",       "dashboard"),
           ("Todos",     "/todos",  "check_circle"),
           ("Form",      "/form",   "edit_note"),
           ("Upload",    "/upload", "upload_file"),
           ("Chat",      "/chat",   "chat"),
       ]:
           cls = "w-full" + (" bg-primary text-white" if label == lively else "")
           ui.button(label,
                     on_click=lambda p=path: ui.navigate.to(p),
                     icon=icon).lessons(cls).props("flat align=left no-caps")


   with ui.header(elevated=True).lessons("items-center justify-between bg-primary"):
       with ui.row().lessons("items-center"):
           ui.button(on_click=drawer.toggle, icon="menu").props("flat colour=white")
           ui.label("🚀 NiceGUI Tutorial").lessons("text-xl font-semibold text-white")
       ui.button(icon="dark_mode", on_click=darkish.toggle).props("flat colour=white")


   with ui.footer().lessons("bg-grey-3 text-black justify-center"):
       ui.label("Built with NiceGUI · Tutorial Demo")

We set up and import all required libraries, then initialize our software state. We outline a central State class to handle todos, metrics, chart knowledge, and chat messages throughout the app. We additionally constructed a reusable format perform that gives navigation, a header, a footer, and darkish mode help for all pages.

@ui.web page("/")
def dashboard():
   page_shell("Dashboard")
   with ui.column().lessons("w-full p-6 gap-6"):
       ui.label("Live Dashboard").lessons("text-3xl font-bold")


       with ui.row().lessons("gap-4 flex-wrap"):
           for key, label, colour, icon in [
               ("users",   "Users",   "primary",  "group"),
               ("revenue", "Revenue", "positive", "attach_money"),
               ("orders",  "Orders",  "warning",  "shopping_cart"),
           ]:
               with ui.card().lessons("w-60"):
                   with ui.row().lessons("items-center justify-between w-full"):
                       ui.label(label).lessons("text-gray-500")
                       ui.icon(icon, dimension="md").lessons(f"text-{colour}")
                   ui.label().lessons(f"text-3xl font-bold text-{colour}") 
                       .bind_text_from(state.metrics, key, backward=lambda v: f"{v:,}")


       with ui.card().lessons("w-full"):
           ui.label("Live stream (updates each second)").lessons("text-lg font-semibold")
           chart = ui.echart({
               "tooltip": {"set off": "axis"},
               "xAxis":   {"sort": "class", "knowledge": record(vary(len(state.sequence)))},
               "yAxis":   {"sort": "worth"},
               "sequence":  [{"data": list(state.series), "type": "line",
                            "smooth": True, "areaStyle": {}}],
           }).lessons("h-64 w-full")


           def tick():
               state.sequence.append(random.uniform(20, 80))
               state.sequence.pop(0)
               chart.choices["series"][0]["data"] = record(state.sequence)
               chart.replace()
               state.metrics["users"]   += random.randint(-2, 4)
               state.metrics["revenue"] += random.randint(-100, 200)
               state.metrics["orders"]  = max(0, state.metrics["orders"] + random.randint(-1, 3))


           ui.timer(1.0, tick)

We create the dashboard web page and construction it with responsive UI elements. We bind metric playing cards straight to the state to allow automated updates and show real-time values. We additionally implement a reside chart utilizing ECharts and dynamically replace each the chart and the metrics utilizing a timer.

@ui.web page("/todos")
def todos_page():
   page_shell("Todos")
   with ui.column().lessons("w-full p-6 gap-4 max-w-4xl mx-auto"):
       ui.label("Todos").lessons("text-3xl font-bold")


       with ui.card().lessons("w-full"):
           with ui.row().lessons("w-full items-center gap-2"):
               task_input   = ui.enter(placeholder="What wants doing?").lessons("flex-grow")
               priority_sel = ui.choose(["Low", "Medium", "High"], worth="Medium").lessons("w-36")


               def add_todo():
                   if not task_input.worth or not task_input.worth.strip():
                       ui.notify("Task can't be empty", sort="warning"); return
                   state.todos.append({
                       "id":       state.next_id,
                       "job":     task_input.worth.strip(),
                       "completed":     False,
                       "precedence": priority_sel.worth,
                   })
                   state.next_id += 1
                   task_input.worth = ""
                   todo_list.refresh()
                   ui.notify("Added!", sort="constructive")


               ui.button("Add", icon="add", on_click=add_todo).props("colour=main")
               task_input.on("keydown.enter", add_todo)


       @ui.refreshable
       def todo_list():
           if not state.todos:
               ui.label("Nothing right here but 🎉").lessons("text-gray-500"); return
           for todo in state.todos:
               with ui.card().lessons("w-full"):
                   with ui.row().lessons("w-full items-center gap-3"):
                       ui.checkbox(worth=todo["done"],
                                   on_change=lambda e, t=todo: t.replace(completed=e.worth))
                       lbl = ui.label(todo["task"]).lessons("flex-grow text-lg")
                       if todo["done"]:
                           lbl.model("text-decoration: line-through; opacity: 0.5")
                       colour = {"High": "pink", "Medium": "orange", "Low": "inexperienced"}[todo["priority"]]
                       ui.badge(todo["priority"], colour=colour)


                       def make_del(t=todo):
                           def _del():
                               state.todos.take away(t)
                               todo_list.refresh()
                               ui.notify("Removed", sort="data")
                           return _del


                       ui.button(icon="delete", on_click=make_del()) 
                           .props("flat colour=pink spherical dense")


       todo_list()

We implement a full CRUD-based todo system with add, replace, and delete performance. We deal with consumer enter validation and dynamically refresh the UI utilizing NiceGUI’s refreshable elements. We additionally improve the UI with checkboxes, badges, and notifications to enhance interactivity and suggestions.

@ui.web page("/kind")
def form_page():
   page_shell("Form")
   with ui.column().lessons("w-full p-6 max-w-2xl mx-auto gap-4"):
       ui.label("Profile Form").lessons("text-3xl font-bold")
       with ui.card().lessons("w-full gap-2"):
           identify  = ui.enter("Name",  validation={"Required":         lambda v: bool(v)})
           e mail = ui.enter("Email", validation={"Must be an e mail": lambda v: "@" in (v or "")})
           age   = ui.quantity("Age", worth=18, min=0, max=120)
           ui.label("Subscription plan").lessons("mt-2 text-gray-600")
           plan  = ui.radio(["Free", "Pro", "Enterprise"], worth="Free").props("inline")
           agree = ui.checkbox("I settle for the phrases")


           async def submit():
               if not (identify.worth and "@" in (e mail.worth or "") and agree.worth):
                   ui.notify("Please repair the shape first", sort="detrimental"); return
               with ui.dialog() as d, ui.card():
                   ui.label("Submitted!").lessons("text-xl font-bold")
                   ui.label(f"Name:  {identify.worth}")
                   ui.label(f"Email: {e mail.worth}")
                   ui.label(f"Age:   {age.worth}")
                   ui.label(f"Plan:  {plan.worth}")
                   ui.button("OK", on_click=d.shut).props("colour=main")
               d.open()


           ui.button("Submit", on_click=submit).props("colour=main")




@ui.web page("/add")
def upload_page():
   page_shell("Upload")
   with ui.column().lessons("w-full p-6 max-w-3xl mx-auto gap-4"):
       ui.label("File Upload").lessons("text-3xl font-bold")
       end result = ui.column().lessons("w-full")


       def handle_upload(e: occasions.UploadEventArguments):
           content material = e.content material.learn()
           with end result:
               with ui.card().lessons("w-full"):
                   ui.label(f"📎 {e.identify}").lessons("font-semibold")
                   ui.label(f"Size: {len(content material):,} bytes · sort: {e.sort}")
                   if e.sort and e.sort.startswith("picture/"):
                       b64 = base64.b64encode(content material).decode()
                       ui.picture(f"knowledge:{e.sort};base64,{b64}").lessons("w-64 rounded")
                   else:
                       strive:
                           ui.code(content material[:500].decode("utf-8", errors="substitute"))
                       besides Exception:
                           move
           ui.notify(f"Uploaded {e.identify}", sort="constructive")


       ui.add(on_upload=handle_upload, a number of=True, auto_upload=True).lessons("w-full")

We construct a kind with validation guidelines and deal with submission utilizing an asynchronous perform. We show consumer enter in a dialog upon profitable submission and guarantee correct validation earlier than processing. We additionally implement a file add characteristic that helps a number of recordsdata and offers picture previews and content material previews for different file sorts.

@ui.web page("/chat")
def chat_page():
   page_shell("Chat")
   with ui.column().lessons("w-full p-6 max-w-3xl mx-auto gap-4"):
       ui.label("Chat (echo bot)").lessons("text-3xl font-bold")


       @ui.refreshable
       def chat_log():
           for m in state.messages:
               ui.chat_message(
                   m["text"],
                   identify="You" if m["role"] == "consumer" else "Bot",
                   despatched=m["role"] == "consumer",
                   stamp=datetime.now().strftime("%H:%M"),
               )


       with ui.card().lessons("w-full"):
           chat_log()


       async def ship():
           textual content = (entry.worth or "").strip()
           if not textual content:
               return
           state.messages.append({"function": "consumer", "textual content": textual content})
           entry.worth = ""
           chat_log.refresh()
           await asyncio.sleep(1)
           reply = f'You stated: "{textual content}" — that's {len(textual content)} characters!'
           state.messages.append({"function": "assistant", "textual content": reply})
           chat_log.refresh()


       with ui.row().lessons("w-full items-center"):
           entry = ui.enter(placeholder="Type a message…") 
               .lessons("flex-grow").on("keydown.enter", ship)
           ui.button(icon="ship", on_click=ship).props("colour=main spherical")




def _free_port() -> int:
   s = socket.socket()
   s.bind(("", 0))
   port = s.getsockname()[1]
   s.shut()
   return port


PORT = _free_port()




def _serve():
   ui.run(host="0.0.0.0", port=PORT, reload=False, present=False,
          title="NiceGUI Tutorial", storage_secret="colab-demo")


threading.Thread(goal=_serve, daemon=True).begin()
time.sleep(4)




strive:
   from google.colab import output
   from google.colab.output import eval_js
   output.serve_kernel_port_as_iframe(PORT, peak="850")
   print(f"App working on port {PORT}")
   print("Open in a new browser tab:")
   print(eval_js(f"google.colab.kernel.proxyPort({PORT})"))
besides ImportError:
   print(f"Not in Colab — open http://localhost:{PORT} in your browser")

We develop an asynchronous chat interface that simulates real-time interplay. We handle chat messages within the shared state and dynamically refresh the chat log after every message. Also, we configure the app to run on a dynamically chosen free port in a background thread and expose it throughout the Colab atmosphere.

In conclusion, we developed a complete understanding of how to construct and construction fashionable net functions utilizing NiceGUI. We introduced collectively a number of superior ideas comparable to state administration, reactive UI updates, routing, asynchronous workflows, and real-time visualization into a single cohesive system. We additionally addressed sensible challenges, comparable to working net servers in pocket book environments and guaranteeing element reusability. This end-to-end implementation demonstrates the pliability and energy of NiceGUI and additionally equips us with the talents to prototype, take a look at, and scale interactive functions effectively.


Check out the Full Codes with Notebook here. Also, be happy to observe us on Twitter and don’t neglect to be a part of our 130k+ ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.

Need to accomplice with us for selling your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar and many others.? Connect with us

The submit How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat appeared first on MarkTechPost.

Similar Posts