Output controls
Outputs create a spot on the webpage to put results from the server.
At a minimum, all UI outputs require an id
argument, which corresponds to the server’s output ID.
For example if you create this UI output:
"my_text") ui.output_text(
Then you could connect it to the server output using the code below.
def server(input, output, session):
@output
@render.text
def my_text():
return "some text to show"
Notice that the name of the function my_text()
matches the output ID; this is how Shiny knows how to connect each of the UI’s outputs with its corresponding logic in the server.
Notice also the relationship between the names ui.output_text()
and @render.text
. Shiny outputs generally follow this pattern of ui.output_XXX()
for the UI and @render.XXX
to decorate the output logic.
Static plot output
Render static plots based on Matplotlib with ui.output_plot()
and @render.plot
. Plotting libraries built on Matplotlib, like seaborn and plotnine are also supported.
The function that @render.plot
decorates typically returns a Matplotlib Figure or plotnine ggplot object, but @render.plot
does support other less common return types, and also supports plots generated through side-effects. See the API reference for more details.
Although ui.output_plot()
holds a static plot, it is possible to respond to user input(s) like hovering, clicking, and/or dragging. See here for an example.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 400
from shiny import ui, render, App
from matplotlib import pyplot as plt
app_ui = ui.page_fluid(
ui.output_plot("a_scatter_plot"),
)
def server(input, output, session):
@output
@render.plot
def a_scatter_plot():
return plt.scatter([1,2,3], [5, 2, 3])
app = App(app_ui, server)
Simple table output
Render various kinds of data frames into an HTML table with ui.output_table()
and @render.table
.
The function that @render.table
decorates typically returns a pandas.DataFrame
, but object(s) that can be coerced to a pandas.DataFrame
via an obj.to_pandas()
method are also supported (this includes Polars data frames and Arrow tables).
For more control over colors, alignment, borders, etc., your server logic may instead return a pandas.Styler
object. See the ui.output_table
for an example.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 300
from pathlib import Path
import pandas as pd
from shiny import ui, render, App
df = pd.read_csv(Path(__file__).parent / "salmon.csv")
app_ui = ui.page_fluid(
ui.output_table("salmon_species"),
)
def server(input, output, session):
@output
@render.table
def salmon_species():
return df
app = App(app_ui, server)
## file: salmon.csv
Common,Scientific
Atlantic,Salmo salar
Chinook,Oncorhynchus tshawytscha
Coho,Oncorhynchus kisutch
Sockeye,Oncorhynchus nerka
Interactive plots
As you’ll learn more about in the section on using ipywidgets, Shiny supports interactive plotting libraries such as plotly, Altair, and more. Here’s a basic example using plotly:
#| standalone: true
#| layout: vertical
#| components: [editor, viewer]
#| viewerHeight: 350
from shiny import ui, App
from shinywidgets import output_widget, render_widget
import plotly.express as px
import plotly.graph_objs as go
df = px.data.tips()
app_ui = ui.page_fluid(
ui.div(
ui.input_select(
"x", label="Variable",
choices=["total_bill", "tip", "size"]
),
ui.input_select(
"color", label="Color",
choices=["smoker", "sex", "day", "time"]
),
class_="d-flex gap-3"
),
output_widget("my_widget")
)
def server(input, output, session):
@output
@render_widget
def my_widget():
fig = px.histogram(
df, x=input.x(), color=input.color(),
marginal="rug"
)
fig.layout.height = 275
return fig
app = App(app_ui, server)
## file: requirements.txt
plotly
pandas
Interactive maps
As you’ll learn more about in the section on using ipywidgets, Shiny supports interactive mapping libraries such as ipyleaflet, pydeck, and more. Here’s a basic example using ipyleaflet:
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 525
from shiny import *
from shinywidgets import output_widget, render_widget
import ipyleaflet as L
basemaps = {
"Satellite": L.basemaps.Gaode.Satellite,
"OpenStreetMap": L.basemaps.OpenStreetMap.Mapnik,
"Stamen.Toner": L.basemaps.Stamen.Toner,
"Stamen.Terrain": L.basemaps.Stamen.Terrain,
"Stamen.Watercolor": L.basemaps.Stamen.Watercolor,
}
app_ui = ui.page_fluid(
ui.input_select(
"basemap", "Choose a basemap",
choices=list(basemaps.keys())
),
output_widget("map")
)
def server(input, output, session):
@output
@render_widget
def map():
basemap = basemaps[input.basemap()]
return L.Map(basemap=basemap, center=[38.128, 2.588], zoom=5)
app = App(app_ui, server)
## file: requirements.txt
ipyleaflet
Other interactive widgets
See the section on using ipywidgets to learn how to effectively use any ipywidgets package inside Shiny.
Text output
Use ui.output_text()
/ @render.text
to render a block of text. Your server logic should return a Python string. You may not use HTML markup or Markdown; see the section on HTML and UI instead.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 100
from shiny import ui, render, App
app_ui = ui.page_fluid(
ui.output_text("my_cool_text")
)
def server(input, output, session):
@output
@render.text
def my_cool_text():
return "hello, world!"
app = App(app_ui, server)
Code output
ui.output_text_verbatim
/ @render.text
(note: not @render.text_verbatim
) is similar to text output, but renders text in a monospace font, and respects newlines and multiple spaces (unlike ui.output_text()
, which collapses all whitespace into a single space, in accordance with HTML’s normal whitespace rule).
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
from shiny import ui, render, App
app_ui = ui.page_fluid(
ui.output_text_verbatim("a_code_block"),
# The p-3 CSS class is used to add padding on all sides of the page
class_="p-3"
)
def server(input, output, session):
@output
@render.text
def a_code_block():
# This function should return a string
return ui.page_navbar.__doc__
app = App(app_ui, server)
HTML and UI output
ui.output_ui()
/ @render.ui
are used to render HTML and UI from the server. These are the same exact objects you use in your app_ui
UI already, and whenever possible, you should put HTML/UI directly into your app_ui
. However, you’ll need to use output_ui
if you want to render HTML/UI dynamically–that is, if you want the HTML to change as inputs and other reactives change.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 250
from shiny import ui, render, App
app_ui = ui.page_fluid(
ui.input_text("message", "Message", value="Hello, world!"),
ui.input_checkbox_group("styles", "Styles", choices=["Bold", "Italic"]),
# The class_ argument is used to enlarge and center the text
ui.output_ui("some_html", class_="display-3 text-center")
)
def server(input, output, session):
@output
@render.ui
def some_html():
x = input.message()
if "Bold" in input.styles():
x = ui.strong(x)
if "Italic" in input.styles():
x = ui.em(x)
return x
app = App(app_ui, server)
When using @render.ui
, your output function can return any of the following:
- A plain string (which will be rendered as plain text, even if it contains HTML markup)
- Any HTML tag object (like
ui.tags.div()
) - Any Shiny UI object, including layouts, inputs, and outputs
You can also write raw HTML or Markdown.
- A string wrapped in
ui.HTML()
, which will be treated as raw HTML markup - A string containing Markdown content, wrapped in
ui.markdown()
, which will be rendered into HTML
Be careful not to use ui.HTML
or ui.markdown
with strings that may be malicious (e.g. based on input from a malicious user, or from a database whose contents could have been influenced by a malicious user), as you could easily introduce a security vulnerability called Cross Site Scripting (XSS). Whenever possible, try to stick to Shiny’s tag and UI functions, which are immune to such attacks.