←back to Blog

How to Build an Advanced Multi-Page Reflex Web Application with Real-Time Database, Dynamic State Management, and Reactive UI

«`html

Understanding the Target Audience

The target audience for this tutorial includes software developers, data scientists, and business analysts who are interested in building web applications using Python. They are likely to have a foundational understanding of programming and web development concepts but may be looking to expand their skills in full-stack development without relying on JavaScript.

Pain Points: This audience often struggles with the complexity of full-stack development, particularly when it comes to managing state and real-time data interactions. They may also find it challenging to integrate various technologies and frameworks effectively.

Goals: Their primary goal is to create efficient, scalable web applications that can handle real-time data and provide a seamless user experience. They are also interested in enhancing their programming skills and exploring new frameworks that simplify the development process.

Interests: This audience is likely interested in topics such as reactive programming, database management, and user interface design. They may also seek out resources that provide practical, hands-on tutorials to apply their learning.

Communication Preferences: They prefer clear, concise tutorials that include code snippets, examples, and explanations of key concepts. Visual aids and step-by-step instructions are also beneficial for enhancing understanding.

Tutorial Overview

In this tutorial, we will build an advanced Reflex web application entirely in Python that runs seamlessly inside Google Colab. The application will serve as a notes-management dashboard featuring two pages, real-time database interactions, filtering, sorting, analytics, and user personalization. We will progressively construct the project in five clean snippets, covering setup, configuration, model and state management, user interface design, and final execution. This approach provides a hands-on understanding of Reflex’s declarative architecture and reactivity system.

Setting Up the Environment

import os, subprocess, sys, pathlib
APP = "reflex_colab_advanced"
os.makedirs(APP, exist_ok=True)
os.chdir(APP)
subprocess.run([sys.executable, "-m", "pip", "install", "-q", "reflex==0.5.9"])

We set up our working environment and install Reflex inside Colab. This step ensures that our app can run smoothly later without dependency issues.

Configuration File

rxconfig = """
import reflex as rx
class Config(rx.Config):
   app_name = "reflex_colab_advanced"
   db_url = "sqlite:///reflex.db"
config = Config()
"""

We define the configuration file that specifies the app name and database connection, connecting Reflex to a local SQLite database to store our notes.

Data Model and State Management

app_py = """
import reflex as rx

class Note(rx.Model, table=True):
   content: str
   tag: str = "general"
   done: bool = False

class State(rx.State):
   user: str = ""
   search: str = ""
   tag_filter: str = "all"
   sort_desc: bool = True
   new_content: str = ""
   new_tag: str = "general"
   toast_msg: str = ""

   def set_user(self, v: str): self.user = v
   def set_search(self, v: str): self.search = v
   def set_tag_filter(self, v: str): self.tag_filter = v
   def set_new_content(self, v: str): self.new_content = v
   def set_new_tag(self, v: str): self.new_tag = v
   def toggle_sort(self): self.sort_desc = not self.sort_desc

   async def add_note(self):
       if self.new_content.strip():
           await Note.create(content=self.new_content.strip(), tag=self.new_tag.strip() or "general")
           self.new_content = ""; self.toast_msg = "Note added"

   async def toggle_done(self, note_id: int):
       note = await Note.get(id=note_id)
       if note:
           await note.update(done=not note.done)

   async def delete_note(self, note_id: int):
       await Note.delete(id=note_id)
       self.toast_msg = "Deleted"

   async def clear_done(self):
       items = await Note.all()
       for n in items:
           if n.done:
               await Note.delete(id=n.id)
       self.toast_msg = "Cleared done notes"

   async def notes_filtered(self):
       items = await Note.all()
       q = self.search.lower()
       if q:
           items = [n for n in items if q in n.content.lower() or q in n.tag.lower()]
       if self.tag_filter != "all":
           items = [n for n in items if n.tag == self.tag_filter]
       items.sort(key=lambda n: n.id, reverse=self.sort_desc)
       return items

   async def stats(self):
       items = await Note.all()
       total = len(items)
       done = len([n for n in items if n.done])
       tags = {}
       for n in items:
           tags[n.tag] = tags.get(n.tag, 0) + 1
       top_tags = sorted(tags.items(), key=lambda x: x[1], reverse=True)[:5]
       return {"total": total, "done": done, "pending": total - done, "tags": top_tags}
"""

We define our data model Note and the reactive State class that controls user input, filtering, and database operations. This structure allows for efficient management of asynchronous actions, such as adding, deleting, and updating notes.

User Interface Design

app_py += """
def sidebar():
   return rx.vstack(
       rx.heading("RC Advanced", size="6"),
       rx.link("Dashboard", href="/"),
       rx.link("Notes Board", href="/board"),
       rx.text("User"),
       rx.input(placeholder="your name", value=State.user, on_change=State.set_user),
       spacing="3", width="15rem", padding="1rem", border_right="1px solid #eee"
   )

async def stats_cards():
   s = await State.stats()
   return rx.hstack(
       rx.box(rx.text("Total"), rx.heading(str(s["total"]), size="5"), padding="1rem", border="1px solid #eee", border_radius="0.5rem"),
       rx.box(rx.text("Done"), rx.heading(str(s["done"]), size="5"), padding="1rem", border="1px solid #eee", border_radius="0.5rem"),
       rx.box(rx.text("Pending"), rx.heading(str(s["pending"]), size="5"), padding="1rem", border="1px solid #eee", border_radius="0.5rem"),
       spacing="4"
   )

def tag_pill(tag: str, count: int = 0):
   return rx.badge(
       f"{tag} ({count})" if count else tag,
       on_click=State.set_tag_filter(tag),
       cursor="pointer",
       color_scheme="blue" if tag == State.tag_filter else "gray"
   )

async def tags_bar():
   s = await State.stats()
   tags = [("all", s["total"])] + s["tags"]
   return rx.hstack(*[tag_pill(t[0], t[1]) for t in tags], spacing="2", wrap="wrap")

def note_row(note: Note):
   return rx.hstack(
       rx.hstack(
           rx.checkbox(is_checked=note.done, on_change=State.toggle_done(note.id)),
           rx.text(note.content, text_decoration="line-through" if note.done else "none"),
       ),
       rx.badge(note.tag, color_scheme="green"),
       rx.button("", on_click=State.delete_note(note.id), color_scheme="red", size="1"),
       justify="between", width="100%"
   )

async def notes_list():
   items = await State.notes_filtered()
   return rx.vstack(*[note_row(n) for n in items], spacing="2", width="100%")
"""

We design modular UI components, including the sidebar, tag filters, and individual note rows. Each UI element reflects state changes in real-time, enhancing user interaction.

Assembling the Application

app_py += """
def dashboard_page():
   return rx.hstack(
       sidebar(),
       rx.box(
           rx.heading("Dashboard", size="8"),
           rx.cond(State.user != "", rx.text(f"Hi {State.user}, here is your activity")),
           rx.vstack(
               rx.suspense(stats_cards, fallback=rx.text("Loading stats...")),
               rx.suspense(tags_bar, fallback=rx.text("Loading tags...")),
               spacing="4"
           ),
           padding="2rem", width="100%"
       ),
       width="100%"
   )

def board_page():
   return rx.hstack(
       sidebar(),
       rx.box(
           rx.heading("Notes Board", size="8"),
           rx.hstack(
               rx.input(placeholder="search...", value=State.search, on_change=State.set_search, width="50%"),
               rx.button("Toggle sort", on_click=State.toggle_sort),
               rx.button("Clear done", on_click=State.clear_done, color_scheme="red"),
               spacing="2"
           ),
           rx.hstack(
               rx.input(placeholder="note content", value=State.new_content, on_change=State.set_new_content, width="60%"),
               rx.input(placeholder="tag", value=State.new_tag, on_change=State.set_new_tag, width="20%"),
               rx.button("Add", on_click=State.add_note),
               spacing="2"
           ),
           rx.cond(State.toast_msg != "", rx.callout(State.toast_msg, icon="info")),
           rx.suspense(notes_list, fallback=rx.text("Loading notes...")),
           padding="2rem", width="100%"
       ),
       width="100%"
   )

app = rx.App()
app.add_page(dashboard_page, route="/", title="RC Dashboard")
app.add_page(board_page, route="/board", title="Notes Board")
app.compile()
"""

Finally, we assemble the dashboard and board pages, compile the entire Reflex app, and run the backend server, bringing our advanced Reflex app to life.

Conclusion

In this tutorial, we developed and ran a full-featured Reflex application that integrates stateful logic, dynamic components, and a persistent SQLite database, all from within Python. This process illustrates how easily we can define both frontend and backend behavior using a unified reactive framework.

For further exploration, check out the GitHub Page for Tutorials, Codes, and Notebooks. Follow us on Twitter and join our community on SubReddit. Subscribe to our Newsletter and connect with us on Telegram.

«`