{"id":88497,"date":"2026-05-06T16:07:30","date_gmt":"2026-05-06T16:07:30","guid":{"rendered":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/"},"modified":"2026-05-06T16:07:30","modified_gmt":"2026-05-06T16:07:30","slug":"how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat","status":"publish","type":"post","link":"https:\/\/youzum.net\/fr\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/","title":{"rendered":"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat"},"content":{"rendered":"<p>In this tutorial, we build a fully interactive, multi-page web application using <a href=\"https:\/\/github.com\/zauberzeug\/nicegui\"><strong>NiceGUI<\/strong><\/a>. We start by setting up the environment and designing a reusable layout that includes navigation, theming, and dark mode support. As we move forward, we implement a live dashboard with real-time metrics and charts, demonstrating reactive bindings and timed updates. We then extend the application with a complete CRUD-based todo system, followed by a validated form with dialogs and user feedback mechanisms. We also incorporate file upload functionality with dynamic previews and conclude the feature set with an asynchronous chat interface that simulates real-time interaction. Also, we ensure that the app runs seamlessly in Colab by using background threading and dynamic port allocation.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">import sys\nimport subprocess\nsubprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"nicegui\"], check=True)\n\n\nimport threading, time, random, asyncio, base64, socket\nfrom datetime import datetime\nfrom nicegui import ui, events\n\n\n\n\nclass State:\n   def __init__(self):\n       self.todos = [\n           {\"id\": 1, \"task\": \"Explore NiceGUI\",      \"done\": True,  \"priority\": \"High\"},\n           {\"id\": 2, \"task\": \"Build a dashboard\",    \"done\": False, \"priority\": \"Medium\"},\n           {\"id\": 3, \"task\": \"Deploy to production\", \"done\": False, \"priority\": \"Low\"},\n       ]\n       self.next_id = 4\n       self.metrics = {\"users\": 1247, \"revenue\": 8420, \"orders\": 53}\n       self.series = [random.uniform(20, 80) for _ in range(20)]\n       self.messages = [{\"role\": \"assistant\",\n                         \"text\": \"Hi! Type something and I will echo it back.\"}]\n\n\nstate = State()\n\n\n\n\ndef page_shell(active: str) -&gt; None:\n   dark = ui.dark_mode()\n   drawer = ui.left_drawer(value=True).classes(\"bg-grey-2\")\n   with drawer:\n       ui.label(\"Navigation\").classes(\"text-lg font-bold p-2\")\n       for label, path, icon in [\n           (\"Dashboard\", \"\/\",       \"dashboard\"),\n           (\"Todos\",     \"\/todos\",  \"check_circle\"),\n           (\"Form\",      \"\/form\",   \"edit_note\"),\n           (\"Upload\",    \"\/upload\", \"upload_file\"),\n           (\"Chat\",      \"\/chat\",   \"chat\"),\n       ]:\n           cls = \"w-full\" + (\" bg-primary text-white\" if label == active else \"\")\n           ui.button(label,\n                     on_click=lambda p=path: ui.navigate.to(p),\n                     icon=icon).classes(cls).props(\"flat align=left no-caps\")\n\n\n   with ui.header(elevated=True).classes(\"items-center justify-between bg-primary\"):\n       with ui.row().classes(\"items-center\"):\n           ui.button(on_click=drawer.toggle, icon=\"menu\").props(\"flat color=white\")\n           ui.label(\"<img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png\" alt=\"\ud83d\ude80\" class=\"wp-smiley\" \/> NiceGUI Tutorial\").classes(\"text-xl font-semibold text-white\")\n       ui.button(icon=\"dark_mode\", on_click=dark.toggle).props(\"flat color=white\")\n\n\n   with ui.footer().classes(\"bg-grey-3 text-black justify-center\"):\n       ui.label(\"Built with NiceGUI \u00b7 Tutorial Demo\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We install and import all required libraries, then initialize our application state. We define a central State class to manage todos, metrics, chart data, and chat messages across the app. We also built a reusable layout function that provides navigation, a header, a footer, and dark mode support for all pages.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">@ui.page(\"\/\")\ndef dashboard():\n   page_shell(\"Dashboard\")\n   with ui.column().classes(\"w-full p-6 gap-6\"):\n       ui.label(\"Live Dashboard\").classes(\"text-3xl font-bold\")\n\n\n       with ui.row().classes(\"gap-4 flex-wrap\"):\n           for key, label, color, icon in [\n               (\"users\",   \"Users\",   \"primary\",  \"group\"),\n               (\"revenue\", \"Revenue\", \"positive\", \"attach_money\"),\n               (\"orders\",  \"Orders\",  \"warning\",  \"shopping_cart\"),\n           ]:\n               with ui.card().classes(\"w-60\"):\n                   with ui.row().classes(\"items-center justify-between w-full\"):\n                       ui.label(label).classes(\"text-gray-500\")\n                       ui.icon(icon, size=\"md\").classes(f\"text-{color}\")\n                   ui.label().classes(f\"text-3xl font-bold text-{color}\") \n                       .bind_text_from(state.metrics, key, backward=lambda v: f\"{v:,}\")\n\n\n       with ui.card().classes(\"w-full\"):\n           ui.label(\"Live stream (updates every second)\").classes(\"text-lg font-semibold\")\n           chart = ui.echart({\n               \"tooltip\": {\"trigger\": \"axis\"},\n               \"xAxis\":   {\"type\": \"category\", \"data\": list(range(len(state.series)))},\n               \"yAxis\":   {\"type\": \"value\"},\n               \"series\":  [{\"data\": list(state.series), \"type\": \"line\",\n                            \"smooth\": True, \"areaStyle\": {}}],\n           }).classes(\"h-64 w-full\")\n\n\n           def tick():\n               state.series.append(random.uniform(20, 80))\n               state.series.pop(0)\n               chart.options[\"series\"][0][\"data\"] = list(state.series)\n               chart.update()\n               state.metrics[\"users\"]   += random.randint(-2, 4)\n               state.metrics[\"revenue\"] += random.randint(-100, 200)\n               state.metrics[\"orders\"]  = max(0, state.metrics[\"orders\"] + random.randint(-1, 3))\n\n\n           ui.timer(1.0, tick)<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We create the dashboard page and structure it with responsive UI components. We bind metric cards directly to the state to enable automatic updates and display real-time values. We also implement a live chart using ECharts and dynamically update both the chart and the metrics using a timer.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">@ui.page(\"\/todos\")\ndef todos_page():\n   page_shell(\"Todos\")\n   with ui.column().classes(\"w-full p-6 gap-4 max-w-4xl mx-auto\"):\n       ui.label(\"Todos\").classes(\"text-3xl font-bold\")\n\n\n       with ui.card().classes(\"w-full\"):\n           with ui.row().classes(\"w-full items-center gap-2\"):\n               task_input   = ui.input(placeholder=\"What needs doing?\").classes(\"flex-grow\")\n               priority_sel = ui.select([\"Low\", \"Medium\", \"High\"], value=\"Medium\").classes(\"w-36\")\n\n\n               def add_todo():\n                   if not task_input.value or not task_input.value.strip():\n                       ui.notify(\"Task cannot be empty\", type=\"warning\"); return\n                   state.todos.append({\n                       \"id\":       state.next_id,\n                       \"task\":     task_input.value.strip(),\n                       \"done\":     False,\n                       \"priority\": priority_sel.value,\n                   })\n                   state.next_id += 1\n                   task_input.value = \"\"\n                   todo_list.refresh()\n                   ui.notify(\"Added!\", type=\"positive\")\n\n\n               ui.button(\"Add\", icon=\"add\", on_click=add_todo).props(\"color=primary\")\n               task_input.on(\"keydown.enter\", add_todo)\n\n\n       @ui.refreshable\n       def todo_list():\n           if not state.todos:\n               ui.label(\"Nothing here yet <img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f389.png\" alt=\"\ud83c\udf89\" class=\"wp-smiley\" \/>\").classes(\"text-gray-500\"); return\n           for todo in state.todos:\n               with ui.card().classes(\"w-full\"):\n                   with ui.row().classes(\"w-full items-center gap-3\"):\n                       ui.checkbox(value=todo[\"done\"],\n                                   on_change=lambda e, t=todo: t.update(done=e.value))\n                       lbl = ui.label(todo[\"task\"]).classes(\"flex-grow text-lg\")\n                       if todo[\"done\"]:\n                           lbl.style(\"text-decoration: line-through; opacity: 0.5\")\n                       color = {\"High\": \"red\", \"Medium\": \"orange\", \"Low\": \"green\"}[todo[\"priority\"]]\n                       ui.badge(todo[\"priority\"], color=color)\n\n\n                       def make_del(t=todo):\n                           def _del():\n                               state.todos.remove(t)\n                               todo_list.refresh()\n                               ui.notify(\"Removed\", type=\"info\")\n                           return _del\n\n\n                       ui.button(icon=\"delete\", on_click=make_del()) \n                           .props(\"flat color=red round dense\")\n\n\n       todo_list()<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We implement a complete CRUD-based todo system with add, update, and delete functionality. We handle user input validation and dynamically refresh the UI using NiceGUI\u2019s refreshable components. We also enhance the UI with checkboxes, badges, and notifications to improve interactivity and feedback.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">@ui.page(\"\/form\")\ndef form_page():\n   page_shell(\"Form\")\n   with ui.column().classes(\"w-full p-6 max-w-2xl mx-auto gap-4\"):\n       ui.label(\"Profile Form\").classes(\"text-3xl font-bold\")\n       with ui.card().classes(\"w-full gap-2\"):\n           name  = ui.input(\"Name\",  validation={\"Required\":         lambda v: bool(v)})\n           email = ui.input(\"Email\", validation={\"Must be an email\": lambda v: \"@\" in (v or \"\")})\n           age   = ui.number(\"Age\", value=18, min=0, max=120)\n           ui.label(\"Subscription plan\").classes(\"mt-2 text-gray-600\")\n           plan  = ui.radio([\"Free\", \"Pro\", \"Enterprise\"], value=\"Free\").props(\"inline\")\n           agree = ui.checkbox(\"I accept the terms\")\n\n\n           async def submit():\n               if not (name.value and \"@\" in (email.value or \"\") and agree.value):\n                   ui.notify(\"Please fix the form first\", type=\"negative\"); return\n               with ui.dialog() as d, ui.card():\n                   ui.label(\"Submitted!\").classes(\"text-xl font-bold\")\n                   ui.label(f\"Name:  {name.value}\")\n                   ui.label(f\"Email: {email.value}\")\n                   ui.label(f\"Age:   {age.value}\")\n                   ui.label(f\"Plan:  {plan.value}\")\n                   ui.button(\"OK\", on_click=d.close).props(\"color=primary\")\n               d.open()\n\n\n           ui.button(\"Submit\", on_click=submit).props(\"color=primary\")\n\n\n\n\n@ui.page(\"\/upload\")\ndef upload_page():\n   page_shell(\"Upload\")\n   with ui.column().classes(\"w-full p-6 max-w-3xl mx-auto gap-4\"):\n       ui.label(\"File Upload\").classes(\"text-3xl font-bold\")\n       result = ui.column().classes(\"w-full\")\n\n\n       def handle_upload(e: events.UploadEventArguments):\n           content = e.content.read()\n           with result:\n               with ui.card().classes(\"w-full\"):\n                   ui.label(f\"<img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f4ce.png\" alt=\"\ud83d\udcce\" class=\"wp-smiley\" \/> {e.name}\").classes(\"font-semibold\")\n                   ui.label(f\"Size: {len(content):,} bytes \u00b7 type: {e.type}\")\n                   if e.type and e.type.startswith(\"image\/\"):\n                       b64 = base64.b64encode(content).decode()\n                       ui.image(f\"data:{e.type};base64,{b64}\").classes(\"w-64 rounded\")\n                   else:\n                       try:\n                           ui.code(content[:500].decode(\"utf-8\", errors=\"replace\"))\n                       except Exception:\n                           pass\n           ui.notify(f\"Uploaded {e.name}\", type=\"positive\")\n\n\n       ui.upload(on_upload=handle_upload, multiple=True, auto_upload=True).classes(\"w-full\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We build a form with validation rules and handle submission using an asynchronous function. We display user input in a dialog upon successful submission and ensure proper validation before processing. We also implement a file upload feature that supports multiple files and provides image previews and content previews for other file types.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">@ui.page(\"\/chat\")\ndef chat_page():\n   page_shell(\"Chat\")\n   with ui.column().classes(\"w-full p-6 max-w-3xl mx-auto gap-4\"):\n       ui.label(\"Chat (echo bot)\").classes(\"text-3xl font-bold\")\n\n\n       @ui.refreshable\n       def chat_log():\n           for m in state.messages:\n               ui.chat_message(\n                   m[\"text\"],\n                   name=\"You\" if m[\"role\"] == \"user\" else \"Bot\",\n                   sent=m[\"role\"] == \"user\",\n                   stamp=datetime.now().strftime(\"%H:%M\"),\n               )\n\n\n       with ui.card().classes(\"w-full\"):\n           chat_log()\n\n\n       async def send():\n           text = (entry.value or \"\").strip()\n           if not text:\n               return\n           state.messages.append({\"role\": \"user\", \"text\": text})\n           entry.value = \"\"\n           chat_log.refresh()\n           await asyncio.sleep(1)\n           reply = f'You said: \"{text}\" \u2014 that is {len(text)} characters!'\n           state.messages.append({\"role\": \"assistant\", \"text\": reply})\n           chat_log.refresh()\n\n\n       with ui.row().classes(\"w-full items-center\"):\n           entry = ui.input(placeholder=\"Type a message\u2026\") \n               .classes(\"flex-grow\").on(\"keydown.enter\", send)\n           ui.button(icon=\"send\", on_click=send).props(\"color=primary round\")\n\n\n\n\ndef _free_port() -&gt; int:\n   s = socket.socket()\n   s.bind((\"\", 0))\n   port = s.getsockname()[1]\n   s.close()\n   return port\n\n\nPORT = _free_port()\n\n\n\n\ndef _serve():\n   ui.run(host=\"0.0.0.0\", port=PORT, reload=False, show=False,\n          title=\"NiceGUI Tutorial\", storage_secret=\"colab-demo\")\n\n\nthreading.Thread(target=_serve, daemon=True).start()\ntime.sleep(4)\n\n\n\n\ntry:\n   from google.colab import output\n   from google.colab.output import eval_js\n   output.serve_kernel_port_as_iframe(PORT, height=\"850\")\n   print(f\"App running on port {PORT}\")\n   print(\"Open in a new browser tab:\")\n   print(eval_js(f\"google.colab.kernel.proxyPort({PORT})\"))\nexcept ImportError:\n   print(f\"Not in Colab \u2014 open http:\/\/localhost:{PORT} in your browser\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We develop an asynchronous chat interface that simulates real-time interaction. We manage chat messages in the shared state and dynamically refresh the chat log after each message. Also, we configure the app to run on a dynamically selected free port in a background thread and expose it within the Colab environment.<\/p>\n<p>In conclusion, we developed a comprehensive understanding of how to build and structure modern web applications using NiceGUI. We brought together multiple advanced concepts such as state management, reactive UI updates, routing, asynchronous workflows, and real-time visualization into a single cohesive system. We also addressed practical challenges, such as running web servers in notebook environments and ensuring component reusability. This end-to-end implementation demonstrates the flexibility and power of NiceGUI and also equips us with the skills to prototype, test, and scale interactive applications efficiently.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<p>Check out\u00a0the\u00a0<strong><a href=\"https:\/\/github.com\/Marktechpost\/AI-Agents-Projects-Tutorials\/blob\/main\/Agentic%20Workflows\/nicegui_full_app_tutorial_Marktechpost.ipynb\" target=\"_blank\" rel=\"noreferrer noopener\">Full Codes with Notebook here<\/a><\/strong>.<strong>\u00a0<\/strong>Also,\u00a0feel free to follow us on\u00a0<strong><a href=\"https:\/\/x.com\/intent\/follow?screen_name=marktechpost\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Twitter<\/mark><\/a><\/strong>\u00a0and don\u2019t forget to join our\u00a0<strong><a href=\"https:\/\/www.reddit.com\/r\/machinelearningnews\/\" target=\"_blank\" rel=\"noreferrer noopener\">130k+ ML SubReddit<\/a><\/strong>\u00a0and Subscribe to\u00a0<strong><a href=\"https:\/\/www.aidevsignals.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">our Newsletter<\/a><\/strong>. Wait! are you on telegram?\u00a0<strong><a href=\"https:\/\/t.me\/machinelearningresearchnews\" target=\"_blank\" rel=\"noreferrer noopener\">now you can join us on telegram as well.<\/a><\/strong><\/p>\n<p>Need to partner with us for promoting your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar etc.?\u00a0<strong><a href=\"https:\/\/forms.gle\/MTNLpmJtsFA3VRVd9\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Connect with us<\/mark><\/a><\/strong><\/p>\n<p>The post <a href=\"https:\/\/www.marktechpost.com\/2026\/05\/06\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\">How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat<\/a> appeared first on <a href=\"https:\/\/www.marktechpost.com\/\">MarkTechPost<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this tutorial, we build a fully interactive, multi-page web application using NiceGUI. We start by setting up the environment and designing a reusable layout that includes navigation, theming, and dark mode support. As we move forward, we implement a live dashboard with real-time metrics and charts, demonstrating reactive bindings and timed updates. We then extend the application with a complete CRUD-based todo system, followed by a validated form with dialogs and user feedback mechanisms. We also incorporate file upload functionality with dynamic previews and conclude the feature set with an asynchronous chat interface that simulates real-time interaction. Also, we ensure that the app runs seamlessly in Colab by using background threading and dynamic port allocation. Copy CodeCopiedUse a different Browser import sys import subprocess subprocess.run([sys.executable, &#8220;-m&#8221;, &#8220;pip&#8221;, &#8220;install&#8221;, &#8220;-q&#8221;, &#8220;nicegui&#8221;], check=True) import threading, time, random, asyncio, base64, socket from datetime import datetime from nicegui import ui, events class State: def __init__(self): self.todos = [ {&#8220;id&#8221;: 1, &#8220;task&#8221;: &#8220;Explore NiceGUI&#8221;, &#8220;done&#8221;: True, &#8220;priority&#8221;: &#8220;High&#8221;}, {&#8220;id&#8221;: 2, &#8220;task&#8221;: &#8220;Build a dashboard&#8221;, &#8220;done&#8221;: False, &#8220;priority&#8221;: &#8220;Medium&#8221;}, {&#8220;id&#8221;: 3, &#8220;task&#8221;: &#8220;Deploy to production&#8221;, &#8220;done&#8221;: False, &#8220;priority&#8221;: &#8220;Low&#8221;}, ] self.next_id = 4 self.metrics = {&#8220;users&#8221;: 1247, &#8220;revenue&#8221;: 8420, &#8220;orders&#8221;: 53} self.series = [random.uniform(20, 80) for _ in range(20)] self.messages = [{&#8220;role&#8221;: &#8220;assistant&#8221;, &#8220;text&#8221;: &#8220;Hi! Type something and I will echo it back.&#8221;}] state = State() def page_shell(active: str) -&gt; None: dark = ui.dark_mode() drawer = ui.left_drawer(value=True).classes(&#8220;bg-grey-2&#8221;) with drawer: ui.label(&#8220;Navigation&#8221;).classes(&#8220;text-lg font-bold p-2&#8221;) for label, path, icon in [ (&#8220;Dashboard&#8221;, &#8220;\/&#8221;, &#8220;dashboard&#8221;), (&#8220;Todos&#8221;, &#8220;\/todos&#8221;, &#8220;check_circle&#8221;), (&#8220;Form&#8221;, &#8220;\/form&#8221;, &#8220;edit_note&#8221;), (&#8220;Upload&#8221;, &#8220;\/upload&#8221;, &#8220;upload_file&#8221;), (&#8220;Chat&#8221;, &#8220;\/chat&#8221;, &#8220;chat&#8221;), ]: cls = &#8220;w-full&#8221; + (&#8221; bg-primary text-white&#8221; if label == active else &#8220;&#8221;) ui.button(label, on_click=lambda p=path: ui.navigate.to(p), icon=icon).classes(cls).props(&#8220;flat align=left no-caps&#8221;) with ui.header(elevated=True).classes(&#8220;items-center justify-between bg-primary&#8221;): with ui.row().classes(&#8220;items-center&#8221;): ui.button(on_click=drawer.toggle, icon=&#8221;menu&#8221;).props(&#8220;flat color=white&#8221;) ui.label(&#8221; NiceGUI Tutorial&#8221;).classes(&#8220;text-xl font-semibold text-white&#8221;) ui.button(icon=&#8221;dark_mode&#8221;, on_click=dark.toggle).props(&#8220;flat color=white&#8221;) with ui.footer().classes(&#8220;bg-grey-3 text-black justify-center&#8221;): ui.label(&#8220;Built with NiceGUI \u00b7 Tutorial Demo&#8221;) We install and import all required libraries, then initialize our application state. We define a central State class to manage todos, metrics, chart data, and chat messages across the app. We also built a reusable layout function that provides navigation, a header, a footer, and dark mode support for all pages. Copy CodeCopiedUse a different Browser @ui.page(&#8220;\/&#8221;) def dashboard(): page_shell(&#8220;Dashboard&#8221;) with ui.column().classes(&#8220;w-full p-6 gap-6&#8221;): ui.label(&#8220;Live Dashboard&#8221;).classes(&#8220;text-3xl font-bold&#8221;) with ui.row().classes(&#8220;gap-4 flex-wrap&#8221;): for key, label, color, icon in [ (&#8220;users&#8221;, &#8220;Users&#8221;, &#8220;primary&#8221;, &#8220;group&#8221;), (&#8220;revenue&#8221;, &#8220;Revenue&#8221;, &#8220;positive&#8221;, &#8220;attach_money&#8221;), (&#8220;orders&#8221;, &#8220;Orders&#8221;, &#8220;warning&#8221;, &#8220;shopping_cart&#8221;), ]: with ui.card().classes(&#8220;w-60&#8221;): with ui.row().classes(&#8220;items-center justify-between w-full&#8221;): ui.label(label).classes(&#8220;text-gray-500&#8243;) ui.icon(icon, size=&#8221;md&#8221;).classes(f&#8221;text-{color}&#8221;) ui.label().classes(f&#8221;text-3xl font-bold text-{color}&#8221;) .bind_text_from(state.metrics, key, backward=lambda v: f&#8221;{v:,}&#8221;) with ui.card().classes(&#8220;w-full&#8221;): ui.label(&#8220;Live stream (updates every second)&#8221;).classes(&#8220;text-lg font-semibold&#8221;) chart = ui.echart({ &#8220;tooltip&#8221;: {&#8220;trigger&#8221;: &#8220;axis&#8221;}, &#8220;xAxis&#8221;: {&#8220;type&#8221;: &#8220;category&#8221;, &#8220;data&#8221;: list(range(len(state.series)))}, &#8220;yAxis&#8221;: {&#8220;type&#8221;: &#8220;value&#8221;}, &#8220;series&#8221;: [{&#8220;data&#8221;: list(state.series), &#8220;type&#8221;: &#8220;line&#8221;, &#8220;smooth&#8221;: True, &#8220;areaStyle&#8221;: {}}], }).classes(&#8220;h-64 w-full&#8221;) def tick(): state.series.append(random.uniform(20, 80)) state.series.pop(0) chart.options[&#8220;series&#8221;][0][&#8220;data&#8221;] = list(state.series) chart.update() state.metrics[&#8220;users&#8221;] += random.randint(-2, 4) state.metrics[&#8220;revenue&#8221;] += random.randint(-100, 200) state.metrics[&#8220;orders&#8221;] = max(0, state.metrics[&#8220;orders&#8221;] + random.randint(-1, 3)) ui.timer(1.0, tick) We create the dashboard page and structure it with responsive UI components. We bind metric cards directly to the state to enable automatic updates and display real-time values. We also implement a live chart using ECharts and dynamically update both the chart and the metrics using a timer. Copy CodeCopiedUse a different Browser @ui.page(&#8220;\/todos&#8221;) def todos_page(): page_shell(&#8220;Todos&#8221;) with ui.column().classes(&#8220;w-full p-6 gap-4 max-w-4xl mx-auto&#8221;): ui.label(&#8220;Todos&#8221;).classes(&#8220;text-3xl font-bold&#8221;) with ui.card().classes(&#8220;w-full&#8221;): with ui.row().classes(&#8220;w-full items-center gap-2&#8243;): task_input = ui.input(placeholder=&#8221;What needs doing?&#8221;).classes(&#8220;flex-grow&#8221;) priority_sel = ui.select([&#8220;Low&#8221;, &#8220;Medium&#8221;, &#8220;High&#8221;], value=&#8221;Medium&#8221;).classes(&#8220;w-36&#8221;) def add_todo(): if not task_input.value or not task_input.value.strip(): ui.notify(&#8220;Task cannot be empty&#8221;, type=&#8221;warning&#8221;); return state.todos.append({ &#8220;id&#8221;: state.next_id, &#8220;task&#8221;: task_input.value.strip(), &#8220;done&#8221;: False, &#8220;priority&#8221;: priority_sel.value, }) state.next_id += 1 task_input.value = &#8220;&#8221; todo_list.refresh() ui.notify(&#8220;Added!&#8221;, type=&#8221;positive&#8221;) ui.button(&#8220;Add&#8221;, icon=&#8221;add&#8221;, on_click=add_todo).props(&#8220;color=primary&#8221;) task_input.on(&#8220;keydown.enter&#8221;, add_todo) @ui.refreshable def todo_list(): if not state.todos: ui.label(&#8220;Nothing here yet &#8220;).classes(&#8220;text-gray-500&#8221;); return for todo in state.todos: with ui.card().classes(&#8220;w-full&#8221;): with ui.row().classes(&#8220;w-full items-center gap-3&#8221;): ui.checkbox(value=todo[&#8220;done&#8221;], on_change=lambda e, t=todo: t.update(done=e.value)) lbl = ui.label(todo[&#8220;task&#8221;]).classes(&#8220;flex-grow text-lg&#8221;) if todo[&#8220;done&#8221;]: lbl.style(&#8220;text-decoration: line-through; opacity: 0.5&#8221;) color = {&#8220;High&#8221;: &#8220;red&#8221;, &#8220;Medium&#8221;: &#8220;orange&#8221;, &#8220;Low&#8221;: &#8220;green&#8221;}[todo[&#8220;priority&#8221;]] ui.badge(todo[&#8220;priority&#8221;], color=color) def make_del(t=todo): def _del(): state.todos.remove(t) todo_list.refresh() ui.notify(&#8220;Removed&#8221;, type=&#8221;info&#8221;) return _del ui.button(icon=&#8221;delete&#8221;, on_click=make_del()) .props(&#8220;flat color=red round dense&#8221;) todo_list() We implement a complete CRUD-based todo system with add, update, and delete functionality. We handle user input validation and dynamically refresh the UI using NiceGUI\u2019s refreshable components. We also enhance the UI with checkboxes, badges, and notifications to improve interactivity and feedback. Copy CodeCopiedUse a different Browser @ui.page(&#8220;\/form&#8221;) def form_page(): page_shell(&#8220;Form&#8221;) with ui.column().classes(&#8220;w-full p-6 max-w-2xl mx-auto gap-4&#8221;): ui.label(&#8220;Profile Form&#8221;).classes(&#8220;text-3xl font-bold&#8221;) with ui.card().classes(&#8220;w-full gap-2&#8221;): name = ui.input(&#8220;Name&#8221;, validation={&#8220;Required&#8221;: lambda v: bool(v)}) email = ui.input(&#8220;Email&#8221;, validation={&#8220;Must be an email&#8221;: lambda v: &#8220;@&#8221; in (v or &#8220;&#8221;)}) age = ui.number(&#8220;Age&#8221;, value=18, min=0, max=120) ui.label(&#8220;Subscription plan&#8221;).classes(&#8220;mt-2 text-gray-600&#8221;) plan = ui.radio([&#8220;Free&#8221;, &#8220;Pro&#8221;, &#8220;Enterprise&#8221;], value=&#8221;Free&#8221;).props(&#8220;inline&#8221;) agree = ui.checkbox(&#8220;I accept the terms&#8221;) async def submit(): if not (name.value and &#8220;@&#8221; in (email.value or &#8220;&#8221;) and agree.value): ui.notify(&#8220;Please fix the form first&#8221;, type=&#8221;negative&#8221;); return with ui.dialog() as d, ui.card(): ui.label(&#8220;Submitted!&#8221;).classes(&#8220;text-xl font-bold&#8221;) ui.label(f&#8221;Name: {name.value}&#8221;) ui.label(f&#8221;Email: {email.value}&#8221;) ui.label(f&#8221;Age: {age.value}&#8221;) ui.label(f&#8221;Plan: {plan.value}&#8221;) ui.button(&#8220;OK&#8221;, on_click=d.close).props(&#8220;color=primary&#8221;) d.open() ui.button(&#8220;Submit&#8221;, on_click=submit).props(&#8220;color=primary&#8221;) @ui.page(&#8220;\/upload&#8221;) def upload_page(): page_shell(&#8220;Upload&#8221;) with ui.column().classes(&#8220;w-full p-6 max-w-3xl mx-auto gap-4&#8221;): ui.label(&#8220;File Upload&#8221;).classes(&#8220;text-3xl font-bold&#8221;) result = ui.column().classes(&#8220;w-full&#8221;) def handle_upload(e: events.UploadEventArguments): content = e.content.read() with result: with ui.card().classes(&#8220;w-full&#8221;): ui.label(f&#8221; {e.name}&#8221;).classes(&#8220;font-semibold&#8221;) ui.label(f&#8221;Size: {len(content):,} bytes \u00b7 type: {e.type}&#8221;) if e.type and e.type.startswith(&#8220;image\/&#8221;): b64 = base64.b64encode(content).decode() ui.image(f&#8221;data:{e.type};base64,{b64}&#8221;).classes(&#8220;w-64 rounded&#8221;) else: try: ui.code(content[:500].decode(&#8220;utf-8&#8243;, errors=&#8221;replace&#8221;)) except Exception: pass ui.notify(f&#8221;Uploaded {e.name}&#8221;, type=&#8221;positive&#8221;) ui.upload(on_upload=handle_upload, multiple=True, auto_upload=True).classes(&#8220;w-full&#8221;) We build a form with validation rules and handle submission using an asynchronous function. We display user input in a dialog upon successful submission and ensure proper validation before processing. We also implement a file upload feature that supports multiple files and provides image previews and content previews for other file types. Copy CodeCopiedUse a different Browser @ui.page(&#8220;\/chat&#8221;) def chat_page(): page_shell(&#8220;Chat&#8221;) with ui.column().classes(&#8220;w-full p-6 max-w-3xl mx-auto gap-4&#8221;): ui.label(&#8220;Chat (echo bot)&#8221;).classes(&#8220;text-3xl font-bold&#8221;) @ui.refreshable def chat_log(): for m in state.messages: ui.chat_message( m[&#8220;text&#8221;], name=&#8221;You&#8221; if m[&#8220;role&#8221;] == &#8220;user&#8221; else &#8220;Bot&#8221;, sent=m[&#8220;role&#8221;] == &#8220;user&#8221;, stamp=datetime.now().strftime(&#8220;%H:%M&#8221;), ) with ui.card().classes(&#8220;w-full&#8221;): chat_log() async def send(): text = (entry.value or &#8220;&#8221;).strip() if not text: return state.messages.append({&#8220;role&#8221;: &#8220;user&#8221;, &#8220;text&#8221;: text}) entry.value = &#8220;&#8221; chat_log.refresh() await asyncio.sleep(1) reply = f&#8217;You said: &#8220;{text}&#8221; \u2014 that is {len(text)} characters!&#8217; state.messages.append({&#8220;role&#8221;: &#8220;assistant&#8221;, &#8220;text&#8221;: reply}) chat_log.refresh() with ui.row().classes(&#8220;w-full items-center&#8221;): entry = ui.input(placeholder=&#8221;Type a message\u2026&#8221;) .classes(&#8220;flex-grow&#8221;).on(&#8220;keydown.enter&#8221;, send)<\/p>","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pmpro_default_level":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_pvb_checkbox_block_on_post":false,"footnotes":""},"categories":[52,5,7,1],"tags":[],"class_list":["post-88497","post","type-post","status-publish","format-standard","hentry","category-ai-club","category-committee","category-news","category-uncategorized","pmpro-has-access"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat - YouZum<\/title>\n<meta name=\"description\" content=\"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/youzum.net\/fr\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat - YouZum\" \/>\n<meta property=\"og:description\" content=\"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19\" \/>\n<meta property=\"og:url\" content=\"https:\/\/youzum.net\/fr\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\" \/>\n<meta property=\"og:site_name\" content=\"YouZum\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/DroneAssociationTH\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-06T16:07:30+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png\" \/>\n<meta name=\"author\" content=\"admin NU\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin NU\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\"},\"author\":{\"name\":\"admin NU\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c\"},\"headline\":\"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat\",\"datePublished\":\"2026-05-06T16:07:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\"},\"wordCount\":592,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#organization\"},\"image\":{\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png\",\"articleSection\":[\"AI\",\"Committee\",\"News\",\"Uncategorized\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\",\"url\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\",\"name\":\"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat - YouZum\",\"isPartOf\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png\",\"datePublished\":\"2026-05-06T16:07:30+00:00\",\"description\":\"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19\",\"breadcrumb\":{\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage\",\"url\":\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png\",\"contentUrl\":\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/youzum.net\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/yousum.gpucore.co\/#website\",\"url\":\"https:\/\/yousum.gpucore.co\/\",\"name\":\"YouSum\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/yousum.gpucore.co\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/yousum.gpucore.co\/#organization\",\"name\":\"Drone Association Thailand\",\"url\":\"https:\/\/yousum.gpucore.co\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png\",\"contentUrl\":\"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png\",\"width\":300,\"height\":300,\"caption\":\"Drone Association Thailand\"},\"image\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/DroneAssociationTH\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c\",\"name\":\"admin NU\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png\",\"contentUrl\":\"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png\",\"caption\":\"admin NU\"},\"url\":\"https:\/\/youzum.net\/fr\/members\/adminnu\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat - YouZum","description":"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/youzum.net\/fr\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/","og_locale":"fr_FR","og_type":"article","og_title":"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat - YouZum","og_description":"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19","og_url":"https:\/\/youzum.net\/fr\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/","og_site_name":"YouZum","article_publisher":"https:\/\/www.facebook.com\/DroneAssociationTH\/","article_published_time":"2026-05-06T16:07:30+00:00","og_image":[{"url":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png","type":"","width":"","height":""}],"author":"admin NU","twitter_card":"summary_large_image","twitter_misc":{"\u00c9crit par":"admin NU","Dur\u00e9e de lecture estim\u00e9e":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#article","isPartOf":{"@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/"},"author":{"name":"admin NU","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c"},"headline":"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat","datePublished":"2026-05-06T16:07:30+00:00","mainEntityOfPage":{"@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/"},"wordCount":592,"commentCount":0,"publisher":{"@id":"https:\/\/yousum.gpucore.co\/#organization"},"image":{"@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage"},"thumbnailUrl":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png","articleSection":["AI","Committee","News","Uncategorized"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/","url":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/","name":"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat - YouZum","isPartOf":{"@id":"https:\/\/yousum.gpucore.co\/#website"},"primaryImageOfPage":{"@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage"},"image":{"@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage"},"thumbnailUrl":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png","datePublished":"2026-05-06T16:07:30+00:00","description":"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19","breadcrumb":{"@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#primaryimage","url":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png","contentUrl":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f680.png"},{"@type":"BreadcrumbList","@id":"https:\/\/youzum.net\/how-to-build-a-fully-interactive-multi-page-nicegui-application-with-real-time-dashboard-crud-operations-file-upload-and-async-chat\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/youzum.net\/"},{"@type":"ListItem","position":2,"name":"How to Build a Fully Interactive Multi-Page NiceGUI Application with Real-Time Dashboard, CRUD Operations, File Upload, and Async Chat"}]},{"@type":"WebSite","@id":"https:\/\/yousum.gpucore.co\/#website","url":"https:\/\/yousum.gpucore.co\/","name":"YouSum","description":"","publisher":{"@id":"https:\/\/yousum.gpucore.co\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/yousum.gpucore.co\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/yousum.gpucore.co\/#organization","name":"Drone Association Thailand","url":"https:\/\/yousum.gpucore.co\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/","url":"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png","contentUrl":"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png","width":300,"height":300,"caption":"Drone Association Thailand"},"image":{"@id":"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/DroneAssociationTH\/"]},{"@type":"Person","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c","name":"admin NU","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/person\/image\/","url":"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png","contentUrl":"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png","caption":"admin NU"},"url":"https:\/\/youzum.net\/fr\/members\/adminnu\/"}]}},"rttpg_featured_image_url":null,"rttpg_author":{"display_name":"admin NU","author_link":"https:\/\/youzum.net\/fr\/members\/adminnu\/"},"rttpg_comment":0,"rttpg_category":"<a href=\"https:\/\/youzum.net\/fr\/category\/ai-club\/\" rel=\"category tag\">AI<\/a> <a href=\"https:\/\/youzum.net\/fr\/category\/committee\/\" rel=\"category tag\">Committee<\/a> <a href=\"https:\/\/youzum.net\/fr\/category\/news\/\" rel=\"category tag\">News<\/a> <a href=\"https:\/\/youzum.net\/fr\/category\/uncategorized\/\" rel=\"category tag\">Uncategorized<\/a>","rttpg_excerpt":"In this tutorial, we build a fully interactive, multi-page web application using NiceGUI. We start by setting up the environment and designing a reusable layout that includes navigation, theming, and dark mode support. As we move forward, we implement a live dashboard with real-time metrics and charts, demonstrating reactive bindings and timed updates. We then\u2026","_links":{"self":[{"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/posts\/88497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/comments?post=88497"}],"version-history":[{"count":0,"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/posts\/88497\/revisions"}],"wp:attachment":[{"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/media?parent=88497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/categories?post=88497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/youzum.net\/fr\/wp-json\/wp\/v2\/tags?post=88497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}