A web development framework for Python that enables building fast, beautiful, and interactive web applications using reactive programming principles.
—
Express mode provides a simplified API for building Shiny applications with automatic UI collection and streamlined syntax. It eliminates the need for explicit UI structure and server function definition, making it ideal for rapid prototyping and simple applications.
# Required imports for this module
from shiny.express import app_opts, wrap_express_app, is_express_app, expressify, output_args
from shiny.express import input, output, ui, render
from shiny import App, Inputs, Outputs, Session
from typing import Callable, Literal, Mapping
from htmltools import Tag, TagList, TagChild
from pathlib import PathDynamic objects that provide session-dependent access to inputs, outputs, and session functionality.
# Express mode core objects (session-dependent)
input: Inputs
"""
Dynamic input accessor that provides reactive access to user inputs.
Equivalent to the input parameter in core mode server functions.
"""
output: Outputs
"""
Dynamic output accessor for assigning rendered content.
Equivalent to the output parameter in core mode server functions.
"""
session: Session
"""
Dynamic session accessor for session management and client communication.
Equivalent to the session parameter in core mode server functions.
"""
render: ModuleType
"""
Rendering functions for creating reactive outputs.
Same as shiny.render module but optimized for Express mode.
"""# Express mode application
from shiny.express import input, output, ui, render
# Direct UI definition (automatically collected)
ui.h1("Express Shiny App")
ui.input_slider("n", "Number of observations:", 0, 100, 20)
# Direct output rendering (automatically assigned)
@render.text
def txt():
return f"The value of n is {input.n()}"
# Access session directly
@render.text
def session_info():
return f"Session ID: {id(session)}"
# Multiple outputs
@render.plot
def histogram():
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(19680801)
data = np.random.randn(input.n())
plt.hist(data, bins=30)
plt.title(f"Histogram of {input.n()} observations")
return plt.gcf()
@render.table
def summary():
import pandas as pd
import numpy as np
np.random.seed(19680801)
data = np.random.randn(input.n())
return pd.DataFrame({
"Statistic": ["Mean", "Std", "Min", "Max"],
"Value": [data.mean(), data.std(), data.min(), data.max()]
})Functions for configuring Express mode applications.
def app_opts(
*,
static_assets: str | Path | Mapping[str, str | Path] | None = None,
bookmark_store: Literal["url", "server", "disable"] | None = None,
debug: bool | None = None,
) -> None:
"""
Configure express app options.
Args:
static_assets: Path to static assets directory or mapping.
bookmark_store: Bookmark storage strategy.
debug: Enable debug mode.
"""
def wrap_express_app(file: Path) -> App:
"""
Wrap an express app file for deployment.
Args:
file: Path to the express app Python file.
Returns:
App instance suitable for deployment.
"""
def is_express_app(app: str, app_dir: str | None) -> bool:
"""
Check if the specified app is an express mode app.
Args:
app: The app filename to check.
app_dir: Directory containing the app file.
Returns:
True if it's an express mode app, False otherwise.
"""
def expressify(fn: Callable[..., T]) -> Callable[..., T]:
"""
Decorator for making functions work with Express mode's automatic display system.
Args:
fn: Function to make compatible with Express mode.
Returns:
Decorated function that works in Express mode.
"""
def output_args(**kwargs: object) -> Callable[[Callable[..., T]], Callable[..., T]]:
"""
Decorator for setting default UI arguments for rendering functions.
Args:
**kwargs: Default arguments to apply to the output UI.
Returns:
Decorator function.
"""# Configure express app
from shiny.express import app_opts, ui, render, input
app_opts(
title="My Express App",
debug=True
)
ui.h1("Configured Express App")
ui.input_text("name", "Enter name:")
@render.text
def greeting():
return f"Hello, {input.name()}!"
# Wrap for deployment (in separate deployment script)
from shiny.express import wrap_express_app
app = wrap_express_app("my_express_app.py")
# Check if in express mode
from shiny.express import is_express_app
if is_express_app():
print("Running in Express mode")
else:
print("Running in Core mode")
# Function-based express app
from shiny.express import expressify
def my_app():
from shiny.express import ui, render, input
ui.h1("Function-based App")
ui.input_slider("x", "Value:", 1, 10, 5)
@render.text
def result():
return f"Value squared: {input.x() ** 2}"
app = expressify(my_app)All UI components from the main shiny.ui module, plus Express-specific additions and context manager versions.
def page_opts(**kwargs: object) -> None:
"""
Configure page-level options for express apps.
Args:
**kwargs: Page configuration options including:
- title: Page title
- lang: Page language
- theme: UI theme
"""
class hold:
"""
Context manager to hold UI element rendering until explicitly released.
"""
def __enter__(self) -> None:
"""Enter hold context."""
def __exit__(self, *args: object) -> None:
"""Exit hold context and render held elements."""Express mode provides context manager versions of layout components for intuitive nested syntax:
# Context manager versions of layout components
sidebar(*args: TagChild, **kwargs: TagAttr) -> ContextManager[None]
layout_sidebar(*args: TagChild, **kwargs: TagAttr) -> ContextManager[None]
card(*args: TagChild, **kwargs: TagAttr) -> ContextManager[None]
accordion(*args: TagChild, **kwargs: TagAttr) -> ContextManager[None]
nav_panel(title: str, *args: TagChild, **kwargs: TagAttr) -> ContextManager[None]
navset_tab(*args: TagChild, **kwargs: TagAttr) -> ContextManager[None]
layout_columns(*args: TagChild, **kwargs: TagAttr) -> ContextManager[None]# Express UI with context managers
from shiny.express import ui, render, input
# Configure page
ui.page_opts(title="Express Layout Demo")
ui.h1("Express Layout Example")
# Sidebar layout with context manager
with ui.layout_sidebar():
with ui.sidebar():
ui.h3("Controls")
ui.input_select("dataset", "Dataset:", ["mtcars", "iris", "diamonds"])
ui.input_slider("n_rows", "Rows to show:", 5, 50, 10)
ui.input_checkbox("show_summary", "Show summary", False)
# Main content area
ui.h2("Data View")
# Cards with context manager
with ui.card():
ui.card_header("Dataset Preview")
@render.data_frame
def data_preview():
# Implementation here
pass
with ui.card():
ui.card_header("Visualization")
@render.plot
def data_plot():
# Implementation here
pass
# Navigation with context manager
with ui.navset_tab():
with ui.nav_panel("Data"):
ui.h3("Data Management")
ui.input_file("upload", "Upload CSV")
with ui.nav_panel("Analysis"):
ui.h3("Statistical Analysis")
with ui.layout_columns():
with ui.card():
ui.card_header("Options")
ui.input_checkbox_group(
"analyses",
"Select analyses:",
["correlation", "regression", "clustering"]
)
with ui.card():
ui.card_header("Results")
@render.text
def analysis_results():
return f"Selected: {input.analyses()}"
# Hold UI rendering
with ui.hold():
# These elements won't render until the context exits
ui.h3("Batch Rendered Elements")
ui.p("This content is rendered together")
for i in range(3):
ui.p(f"Item {i + 1}")Module system adapted for Express mode applications.
module: object
"""
Module system for Express apps with decorators and utilities.
Provides:
- @module.ui decorator for module UI functions
- @module.server decorator for module server functions
- current_namespace() function
- resolve_id() function
"""# Express module definition
from shiny.express import module, ui, render, input
# Module UI function
@module.ui
def counter_ui():
ui.h3("Counter Module")
ui.input_action_button("increment", "Increment")
ui.input_action_button("reset", "Reset")
ui.output_text("count")
# Module server function
@module.server
def counter_server(input, output, session):
count = reactive.value(0)
@reactive.effect
@reactive.event(input.increment)
def _():
count.set(count.get() + 1)
@reactive.effect
@reactive.event(input.reset)
def _():
count.set(0)
@output
@render.text
def count():
return f"Count: {count()}"
# Use module in main app
from shiny.express import ui
ui.h1("App with Modules")
# Include multiple instances of the module
counter_ui("counter1")
counter_server("counter1")
ui.hr()
counter_ui("counter2")
counter_server("counter2")Express-compatible versions of interactive components.
class Chat:
"""
Express-compatible chat interface.
Provides real-time chat functionality optimized for Express mode
with automatic UI integration and simplified message handling.
"""
def __init__(
self,
id: str,
messages: list[ChatMessage] | None = None,
**kwargs: object
): ...
def send_message(self, message: str, user: str = "assistant") -> None:
"""Send a message to the chat."""
def clear_messages(self) -> None:
"""Clear all chat messages."""
class MarkdownStream:
"""
Express-compatible markdown streaming component.
Provides streaming markdown rendering with real-time updates
optimized for Express mode applications.
"""
def __init__(self, id: str, **kwargs: object): ...
def write(self, content: str) -> None:
"""Write content to the markdown stream."""
def clear(self) -> None:
"""Clear the markdown stream."""# Express chat application
from shiny.express import ui, render, input, Chat
ui.h1("Express Chat Demo")
# Create chat interface
chat = Chat(
id="main_chat",
messages=[
{"user": "assistant", "message": "Hello! How can I help you?"},
]
)
ui.input_text("user_message", "Type your message:", placeholder="Enter message...")
ui.input_action_button("send", "Send")
@reactive.effect
@reactive.event(input.send)
def handle_message():
message = input.user_message()
if message.strip():
# Add user message
chat.send_message(message, user="user")
# Simple echo response
response = f"You said: {message}"
chat.send_message(response, user="assistant")
# Clear input (would need session.send_input_message in real app)
# Streaming markdown example
from shiny.express import MarkdownStream
ui.h2("Streaming Content")
markdown_stream = MarkdownStream("content_stream")
ui.input_action_button("start_stream", "Start Streaming")
ui.input_action_button("clear_stream", "Clear")
@reactive.effect
@reactive.event(input.start_stream)
def start_streaming():
import asyncio
async def stream_content():
content_pieces = [
"# Streaming Demo\n\n",
"This content is being **streamed** in real-time.\n\n",
"- First item\n",
"- Second item\n",
"- Third item\n\n",
"## Code Example\n\n",
"```python\n",
"def hello():\n",
" print('Hello, World!')\n",
"```\n\n",
"Streaming complete! 🎉"
]
for piece in content_pieces:
markdown_stream.write(piece)
await asyncio.sleep(0.5)
asyncio.create_task(stream_content())
@reactive.effect
@reactive.event(input.clear_stream)
def clear_content():
markdown_stream.clear()from shiny import App, ui, render, Inputs, Outputs, Session
# Explicit UI definition
app_ui = ui.page_fluid(
ui.h1("Core Mode App"),
ui.input_slider("n", "Number:", 1, 10, 5),
ui.output_text("result")
)
# Explicit server function
def server(input: Inputs, output: Outputs, session: Session):
@output
@render.text
def result():
return f"Value: {input.n()}"
# Explicit app creation
app = App(app_ui, server)from shiny.express import ui, render, input
# Direct UI definition (automatically collected)
ui.h1("Express Mode App")
ui.input_slider("n", "Number:", 1, 10, 5)
# Direct output rendering (automatically assigned)
@render.text
def result():
return f"Value: {input.n()}"
# App automatically created from collected UI and outputsis_express_app() to write code that works in both modes# Adaptive code for both modes
from shiny.express import is_express_app
if is_express_app():
from shiny.express import input, output, session
else:
# In core mode, these come from server function parameters
pass
# Function that works in both modes
def get_user_greeting():
if is_express_app():
from shiny.express import input
return f"Hello, {input.username()}!"
else:
# Core mode would need input parameter passed in
raise RuntimeError("Must be called from within server function")Install with Tessl CLI
npx tessl i tessl/pypi-shiny