Routing
PyView uses Starlette’s routing under the hood—no need to learn a new system if you’re already familiar with it. You get flexible URL patterns with automatic type conversion for path parameters.
Basic Route Registration
Register LiveViews with add_live_view():
from pyview import PyViewfrom views.counter import CounterLiveViewfrom views.users import UserListLiveView, UserDetailLiveView
app = PyView()
app.add_live_view("/", CounterLiveView)app.add_live_view("/users", UserListLiveView)app.add_live_view("/users/{id}", UserDetailLiveView)Path Parameters
Use curly braces to capture URL segments. Starlette’s converters handle type conversion automatically:
# Matches /users/123, /users/456, etc.app.add_live_view("/users/{id:int}", UserDetailLiveView)
# Matches /posts/hello-world, /posts/my-first-post, etc.app.add_live_view("/posts/{slug:str}", PostLiveView)
# Matches /files/documents/report.pdf (captures full path)app.add_live_view("/files/{path:path}", FileLiveView)Accessing Path Parameters
Path parameters are automatically merged with query parameters and passed to handle_params(). Use typed parameters for clean access:
class UserDetailLiveView(LiveView[UserContext]): async def mount(self, socket: LiveViewSocket[UserContext], session): socket.context = {"user": None}
async def handle_params(self, socket: LiveViewSocket[UserContext], id: int): # 'id' is already converted to int from the URL /users/123 user = await load_user(id) socket.context["user"] = userPath parameters take precedence over query parameters if there’s a name conflict.
Multiple Path Parameters
# Route: /orgs/{org}/projects/{project_id}app.add_live_view("/orgs/{org}/projects/{project_id}", ProjectLiveView)
class ProjectLiveView(LiveView[ProjectContext]): async def handle_params(self, socket, org: str, project_id: int): project = await load_project(org, project_id) socket.context["project"] = projectRoute Priority
When multiple routes could match a URL, static routes take precedence over parameterized routes:
app.add_live_view("/users/new", NewUserLiveView) # Static - matched firstapp.add_live_view("/users/{id}", UserDetailLiveView) # ParameterizedA request to /users/new goes to NewUserLiveView, while /users/123 goes to UserDetailLiveView.
Trailing Slashes
PyView handles trailing slashes gracefully. Both /users and /users/ will match a route registered as /users.
Combining with Query Parameters
Path and query parameters work together seamlessly:
# URL: /users/123?tab=settings&mode=edit# Route: /users/{id:int}
class UserDetailLiveView(LiveView[UserContext]): async def handle_params(self, socket, id: int, tab: str = "profile", mode: str = "view"): # id = 123 (from path) # tab = "settings" (from query) # mode = "edit" (from query) socket.context.update({ "user": await load_user(id), "active_tab": tab, "edit_mode": mode == "edit" })Static Files
Mount static files separately using Starlette’s StaticFiles:
from starlette.staticfiles import StaticFiles
app = PyView()
# Serve files from ./static at /static URLapp.mount("/static", StaticFiles(directory="static"), name="static")
# Or from package resourcesapp.mount( "/static", StaticFiles(packages=[("myapp", "static")]), name="static")Route Organization
For larger applications, organize routes in a list:
from pyview import PyViewfrom views import ( HomeLiveView, UserListLiveView, UserDetailLiveView, SettingsLiveView,)
routes = [ ("/", HomeLiveView), ("/users", UserListLiveView), ("/users/{id:int}", UserDetailLiveView), ("/settings", SettingsLiveView),]
app = PyView()
for path, view in routes: app.add_live_view(path, view)Non-LiveView Routes
You can add regular Starlette routes alongside LiveViews for APIs, webhooks, or static pages:
from starlette.routing import Routefrom starlette.responses import JSONResponse
async def health_check(request): return JSONResponse({"status": "ok"})
app = PyView()app.routes.append(Route("/health", health_check))app.add_live_view("/", HomeLiveView)Custom Root Template
Customize the HTML shell that wraps your LiveViews:
from pyview import PyView, defaultRootTemplatefrom markupsafe import Markup
css = Markup("""<link rel="stylesheet" href="/static/styles.css"><script src="https://cdn.tailwindcss.com"></script>""")
app = PyView()app.rootTemplate = defaultRootTemplate(css=css)See Socket and Context - Navigation for navigating between routes programmatically.