Transition to Core
This article digs into the syntax differences translation Express and Core apps as well as a translation guide to help you move from Express to Core.
The quickest way to tell whether an app is an Express app is the presence of shiny.express in the import statements. Common Express imports like from shiny.express import ui, input highlight the main difference from Core: expression of user interfaces (ui) and where input values come from. You’ll also commonly see Core imports like from shiny import reactive in Express apps, highlighting the fact that things like reactivity work the same way in both modes.
To dig into more specifics, consider the following app that just displays a slider value, and notice the following:
- Core requires an
App()object, which in turn requires a UI definition and server function. - Core UI starts with a
ui.page_*()call to create a page layout. It also requires output containers (i.e.,ui.output_*()) in the UI with ids that match the correspondingrenderfunction.- In Express, these page and output containers are implicit.1
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 150
from shiny import App, render, ui
app_ui = ui.page_fixed(
ui.input_slider("val", "Slider label", min=0, max=100, value=50),
ui.output_text_verbatim("slider_val")
)
def server(input, output, session):
@render.text
def slider_val():
return f"Slider value: {input.val()}"
app = App(app_ui, server)
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 150
from shiny.express import input, render, ui
ui.input_slider("val", "Slider label", min=0, max=100, value=50)
@render.text
def slider_val():
return f"Slider value: {input.val()}"
Now, suppose we add a UI component that takes other components as children, like ui.layout_columns(). In Core, this is done by nesting pure function calls. However, in Express, UI components that take other UI components as children are context managers, so we use with statements instead.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 150
from shiny import App, render, ui
app_ui = ui.page_fixed(
ui.layout_columns(
ui.input_slider("val", "Slider label", min=0, max=100, value=50),
ui.output_text_verbatim("slider_val")
)
)
def server(input, output, session):
@render.text
def slider_val():
return f"Slider value: {input.val()}"
app = App(app_ui, server)
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 150
from shiny.express import input, render, ui
with ui.layout_columns():
ui.input_slider("val", "Slider label", min=0, max=100, value=50)
@render.text
def slider_val():
return f"Slider value: {input.val()}"
Terminal UI components (e.g. ui.input_slider()); that is, components that usually don’t take other UI components as children, are not context managers in Express.
In Express, HTML tags can be used as both context managers and/or pure functions. For example, ui.div(ui.h1("Hello world!")) is also equivalent to with ui.div(): ui.h1("Hello world!").
Translation guide
When translating an Express app to Core, the following steps are recommended:
- Replace Express imports with Core imports (e.g.,
from shiny.express import ui->from shiny import ui). - Add
from shiny import App. - Add the following just below the imports:
app_ui = ui.page_fixed(
# static UI here
)
def server(input, output, session):
# render/reactive logic here
...
app = App(app_ui, server)- Then, start moving the “top-level” Express logic into the UI/server:
- Identify
@renderand@reactivefunctions and move them insideserverfunction. - Add
ui.output_*()containers toapp_uifor each@renderfunction. - Move
uicomponents (i.e., inputs and layout) and move them inside theapp_ui.- Remember that, in Core, layout components like
ui.layout_columns()are pure functions, not context managers.
- Remember that, in Core, layout components like
- If your Express app has top-level
ui.sidebar()and/orui.nav_panel()components, you’ll need to also changeui.page_fixed()toui.page_sidebar()/ui.page_navbar().
Footnotes
In Express, page layout options can be controlled via
ui.page_opts()and (at least some, for now) output containers can be controlled through their respective@render.*()decorators.↩︎