2025, Nov 05 15:00
Add a logo to a Shiny for Python navbar by correctly serving static files with static_assets
Learn why your Shiny for Python navbar logo doesn’t show and how to fix it: serve a static folder, map it with static_assets, and reference images via static/.
Embedding a logo in a Shiny for Python navbar looks straightforward, but by default the app won’t serve files from disk unless it’s explicitly told where those assets live. If the image path isn’t mapped to a static directory, the browser request won’t be fulfilled and the logo will simply not appear.
Minimal example that fails to display the logo
from shiny import App, ui
ui_shell = ui.page_navbar(
ui.nav_panel("Bar", ui.h2("Bar plot")),
ui.nav_panel("Map", ui.h2("Cloropleth")),
ui.nav_spacer(),
ui.nav_control(ui.input_dark_mode(id="toggle_dm")),
title=ui.tags.a(ui.tags.img(src="logo.png", height="30px"), "", href="#")
)
app_runner = App(ui_shell, server=None)
if __name__ == "__main__":
app_runner.run()
The layout is valid, the image is referenced, and the app runs. The missing piece is static file serving: the runtime doesn’t know where to fetch logo.png from.
What’s actually going on
Static files such as images, CSS, and JavaScript aren’t automatically exposed by the server. You need to declare a folder that Shiny should serve and mount it under a URL prefix. The markup that references the file must use that prefix so the server can route those requests to the right place.
The fix
Create a folder named static alongside your script and move logo.png into it. In the app code, define a path to that folder with pathlib, reference the image using a path that begins with static/, and pass a mapping to static_assets when instantiating App so the server knows how to serve those files.
from pathlib import Path
from shiny import App, ui
public_dir = Path(__file__).parent / "static"
ui_shell = ui.page_navbar(
ui.nav_panel("Bar", ui.h2("Bar plot")),
ui.nav_panel("Map", ui.h2("Cloropleth")),
ui.nav_spacer(),
ui.nav_control(ui.input_dark_mode(id="toggle_dm")),
title=ui.tags.a(ui.tags.img(src="static/logo.png", height="30px"), "", href="#")
)
app_runner = App(ui_shell, server=None, static_assets={"/static": public_dir})
if __name__ == "__main__":
app_runner.run()
With this setup, requests to paths that start with static/ in your HTML resolve to the files placed inside the designated static directory.
Why this matters
Being explicit about static assets avoids surprises in production and during refactors. The application becomes predictable: the server exposes a controlled file tree, and the UI references it through a clear, stable URL path. This same pattern scales to additional images and other front-end resources you may add later.
Takeaways
Point the app to a dedicated static folder, reference files under that path in your HTML, and wire the mapping through the static_assets argument when creating the App. Once those pieces are in place, your logo and any other assets will load reliably.
The article is based on a question from StackOverflow by GSandro_Strongs and an answer by Detlef.