Card
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 400
from shiny import App, render, ui
import matplotlib.pyplot as plt
app_ui = ui.page_fluid(
ui.card(
ui.card_header(
"Sales Trend",
class_="bg-dark",
),
ui.output_plot("plot"),
full_screen=True,
height="350px",
)
)
def server(input, output, session):
@render.plot
def plot():
# Generate sample sales data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
sales = [45, 52, 48, 61, 58, 67, 71, 69, 74, 78, 82, 88]
fig, ax = plt.subplots(figsize=(7, 3.5))
ax.plot(months, sales, marker='o', linewidth=2, markersize=5, color='steelblue')
ax.fill_between(range(len(months)), sales, alpha=0.3, color='steelblue')
ax.set_title("Monthly Sales Trend", fontsize=10, pad=8)
ax.set_xlabel("Month", fontsize=8)
ax.set_ylabel("Sales ($K)", fontsize=8)
ax.grid(True, alpha=0.3)
ax.tick_params(labelsize=7)
plt.xticks(rotation=45)
plt.tight_layout(pad=2.0)
return fig
app = App(app_ui, server)from shiny.express import render, ui
import matplotlib.pyplot as plt
with ui.card(full_screen=True, height="350px"):
ui.card_header(
"Sales Trend",
class_="bg-dark",
)
@render.plot
def plot():
# Generate sample sales data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
sales = [45, 52, 48, 61, 58, 67, 71, 69, 74, 78, 82, 88]
fig, ax = plt.subplots(figsize=(7, 3.5))
ax.plot(months, sales, marker='o', linewidth=2, markersize=5, color='steelblue')
ax.fill_between(range(len(months)), sales, alpha=0.3, color='steelblue')
ax.set_title("Monthly Sales Trend", fontsize=10, pad=8)
ax.set_xlabel("Month", fontsize=8)
ax.set_ylabel("Sales ($K)", fontsize=8)
ax.grid(True, alpha=0.3)
ax.tick_params(labelsize=7)
plt.xticks(rotation=45)
plt.tight_layout(pad=2.0)
return fig from shiny import App, render, ui
import matplotlib.pyplot as plt
app_ui = ui.page_fluid(
ui.card(
ui.card_header(
"Sales Trend",
class_="bg-dark",
),
ui.output_plot("plot"),
full_screen=True,
height="350px",
)
)
def server(input, output, session):
@render.plot
def plot():
# Generate sample sales data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
sales = [45, 52, 48, 61, 58, 67, 71, 69, 74, 78, 82, 88]
fig, ax = plt.subplots(figsize=(7, 3.5))
ax.plot(months, sales, marker='o', linewidth=2, markersize=5, color='steelblue')
ax.fill_between(range(len(months)), sales, alpha=0.3, color='steelblue')
ax.set_title("Monthly Sales Trend", fontsize=10, pad=8)
ax.set_xlabel("Month", fontsize=8)
ax.set_ylabel("Sales ($K)", fontsize=8)
ax.grid(True, alpha=0.3)
ax.tick_params(labelsize=7)
plt.xticks(rotation=45)
plt.tight_layout(pad=2.0)
return fig
app = App(app_ui, server)Relevant Functions
-
ui.card
ui.card(*args, full_screen=False, height=None, max_height=None, min_height=None, fill=True, class_=None, **kwargs) -
ui.card_header
ui.card_header(*args, container=..., **kwargs) -
ui.card_body
ui.card_body(*args, fillable=True, min_height=None, max_height=None, max_height_full_screen=None, height=None, padding=None, gap=None, fill=True, class_=None, **kwargs) -
ui.card_footer
ui.card_footer(*args, **kwargs)
Details
A card is a rectangular container with borders and padding that groups related UI elements together.
To add a card to your app:
- Add
ui.card()to your UI. - Place UI elements inside the card as direct children, or use
ui.card_header(),ui.card_body(), andui.card_footer()to organize content into distinct sections.
If you don’t use ui.card_body(), any direct children of ui.card() that aren’t card items (like ui.card_header() or ui.card_footer()) are automatically wrapped in an implicit ui.card_body().
Card customization
Cards and card items accept Bootstrap utility classes for customization. Use the class_ parameter to apply classes for colors, text, borders, and more.
Card size
By default, a card grows to accommodate its contents. Set the height or max_height parameter to control the card’s size. If your card contains large amounts of text, tables, or other content, setting a height or max_height enables scrolling. When laying out multiple cards, let the layout function determine the card heights rather than setting fixed heights.
Use full_screen=True to add an icon that expands the card to fill the browser window. Pair max_height with full_screen=True to provide an expansion icon. When users click the icon, the card fills the browser window. The max_height constraint only applies to the normal view, not the full-screen view.
Filling layouts
Cards support filling layouts where outputs automatically resize to fit the available space. When a fill item like ui.output_plot() appears as a direct child of a ui.card_body(), it resizes to match the card’s height. For example, setting height="250px" on a card reduces the plot’s height from its default 400px to approximately 200px. When expanded to full-screen, the plot grows to match the card’s new size.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 500
from shiny import App, render, ui
import matplotlib.pyplot as plt
app_ui = ui.page_fluid(
ui.card(
ui.card_header("Sales Trend"),
ui.output_plot("plot"),
height="400px",
full_screen=True
)
)
def server(input, output, session):
@render.plot
def plot():
# Generate sample data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
sales = [45, 52, 48, 61, 58, 67]
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(months, sales, marker='o', linewidth=2, markersize=8, color='steelblue')
ax.set_title("Monthly Sales Trend")
ax.set_xlabel("Month")
ax.set_ylabel("Sales ($K)")
ax.grid(True, alpha=0.3)
return fig
app = App(app_ui, server)from shiny.express import ui, render
import matplotlib.pyplot as plt
with ui.card(height="400px", full_screen=True):
ui.card_header("Sales Trend")
@render.plot
def plot():
# Generate sample data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
sales = [45, 52, 48, 61, 58, 67]
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(months, sales, marker='o', linewidth=2, markersize=8, color='steelblue')
ax.set_title("Monthly Sales Trend")
ax.set_xlabel("Month")
ax.set_ylabel("Sales ($K)")
ax.grid(True, alpha=0.3)
return figfrom shiny import App, render, ui
import matplotlib.pyplot as plt
app_ui = ui.page_fluid(
ui.card(
ui.card_header("Sales Trend"),
ui.output_plot("plot"),
height="400px",
full_screen=True
)
)
def server(input, output, session):
@render.plot
def plot():
# Generate sample data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
sales = [45, 52, 48, 61, 58, 67]
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(months, sales, marker='o', linewidth=2, markersize=8, color='steelblue')
ax.set_title("Monthly Sales Trend")
ax.set_xlabel("Month")
ax.set_ylabel("Sales ($K)")
ax.grid(True, alpha=0.3)
return fig
app = App(app_ui, server)Tabbed card
Use ui.navset_card_underline() inside a card to create tabbed content.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 250
from shiny import App, ui
app_ui = ui.page_fluid(
ui.navset_card_underline(
ui.nav_spacer(),
ui.nav_panel(
"Overview",
"This project has 3 milestones and 12 tasks.",
ui.br(),
"Current status: On track",
),
ui.nav_panel(
"Team",
ui.tags.ul(
ui.tags.li("Alice (Lead)"),
ui.tags.li("Bob (Developer)"),
ui.tags.li("Carol (Designer)"),
),
),
ui.nav_panel(
"Timeline",
"Start: January 2026",
ui.br(),
"Expected completion: June 2026",
),
title="Project Dashboard",
),
full_screen=True,
height="300px",
)
def server(input, output, session):
pass
app = App(app_ui, server)from shiny.express import ui
ui.page_opts(full_screen=True, height="300px")
with ui.navset_card_underline(title="Project Dashboard"):
ui.nav_spacer()
with ui.nav_panel("Overview"):
"This project has 3 milestones and 12 tasks."
ui.br()
"Current status: On track"
with ui.nav_panel("Team"):
ui.tags.ul(
ui.tags.li("Alice (Lead)"),
ui.tags.li("Bob (Developer)"),
ui.tags.li("Carol (Designer)"),
)
with ui.nav_panel("Timeline"):
"Start: January 2026"
ui.br()
"Expected completion: June 2026"from shiny import App, ui
app_ui = ui.page_fluid(
ui.navset_card_underline(
ui.nav_spacer(),
ui.nav_panel(
"Overview",
"This project has 3 milestones and 12 tasks.",
ui.br(),
"Current status: On track",
),
ui.nav_panel(
"Team",
ui.tags.ul(
ui.tags.li("Alice (Lead)"),
ui.tags.li("Bob (Developer)"),
ui.tags.li("Carol (Designer)"),
),
),
ui.nav_panel(
"Timeline",
"Start: January 2026",
ui.br(),
"Expected completion: June 2026",
),
title="Project Dashboard",
),
full_screen=True,
height="300px",
)
def server(input, output, session):
pass
app = App(app_ui, server)Multiple cards
Use ui.layout_columns() to arrange multiple cards in a multi-column layout. Cards in the same row automatically share the same height. See the variations below for examples of multiple-card layouts.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 500
import pandas as pd
from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.layout_columns(
ui.card(
ui.card_header(
"Sales Data",
ui.toolbar(
ui.toolbar_input_select(
id="region_filter",
label="Region",
choices=["All", "North", "South", "East", "West"],
selected="All"
)
)
),
ui.card_body(
ui.output_data_frame("sales_table")
),
full_screen=True
),
ui.card(
ui.card_header("Key Insights"),
ui.card_body(
ui.markdown("""
**Performance Summary**
- Sales increased by 96% over 6 months
- April showed a temporary dip
- Strong recovery in May and June
- Current trajectory suggests continued growth
*Data updated: June 2024*
""")
),
full_screen=True
)
)
)
def server(input, output, session):
@render.data_frame
def sales_table():
data = pd.DataFrame({
"Product": ["Widget A", "Widget B", "Widget C", "Widget D", "Widget E"],
"Region": ["North", "South", "East", "West", "North"],
"Sales": [15_200, 12_800, 9_500, 11_300, 8_700],
"Growth": ["+12%", "+8%", "+5%", "+10%", "+3%"]
})
if input.region_filter() != "All":
data = data[data["Region"] == input.region_filter()]
return data
app = App(app_ui, server)import pandas as pd
from shiny.express import input, render, ui
with ui.layout_columns():
with ui.card(full_screen=True):
with ui.card_header():
"Sales Data"
with ui.toolbar():
ui.toolbar_input_select(
id="region_filter",
label="Region",
choices=["All", "North", "South", "East", "West"],
selected="All"
)
@render.data_frame
def sales_table():
data = pd.DataFrame({
"Product": ["Widget A", "Widget B", "Widget C", "Widget D", "Widget E"],
"Region": ["North", "South", "East", "West", "North"],
"Sales": [15_200, 12_800, 9_500, 11_300, 8_700],
"Growth": ["+12%", "+8%", "+5%", "+10%", "+3%"]
})
if input.region_filter() != "All":
data = data[data["Region"] == input.region_filter()]
return data
with ui.card(full_screen=True):
ui.card_header("Key Insights")
ui.markdown("""
**Performance Summary**
- Sales increased by 96% over 6 months
- April showed a temporary dip
- Strong recovery in May and June
- Current trajectory suggests continued growth
*Data updated: June 2024*
""")import pandas as pd
from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.layout_columns(
ui.card(
ui.card_header(
"Sales Data",
ui.toolbar(
ui.toolbar_input_select(
id="region_filter",
label="Region",
choices=["All", "North", "South", "East", "West"],
selected="All"
)
)
),
ui.card_body(
ui.output_data_frame("sales_table")
),
full_screen=True
),
ui.card(
ui.card_header("Key Insights"),
ui.card_body(
ui.markdown("""
**Performance Summary**
- Sales increased by 96% over 6 months
- April showed a temporary dip
- Strong recovery in May and June
- Current trajectory suggests continued growth
*Data updated: June 2024*
""")
),
full_screen=True
)
)
)
def server(input, output, session):
@render.data_frame
def sales_table():
data = pd.DataFrame({
"Product": ["Widget A", "Widget B", "Widget C", "Widget D", "Widget E"],
"Region": ["North", "South", "East", "West", "North"],
"Sales": [15_200, 12_800, 9_500, 11_300, 8_700],
"Growth": ["+12%", "+8%", "+5%", "+10%", "+3%"]
})
if input.region_filter() != "All":
data = data[data["Region"] == input.region_filter()]
return data
app = App(app_ui, server)Toolbars in cards
Add toolbars to card headers and footers to provide interactive controls. Use ui.toolbar() within a ui.card_header() or ui.card_footer() to create a compact row of buttons or inputs. Toolbars are particularly useful for actions like refreshing data, downloading reports, or accessing settings.
By default, toolbars align to the right. Set align="left" to position controls on the left side instead. Use ui.toolbar_spacer() and ui.toolbar_divider() to manipulate toolbar visual spacing.