This repo contains minimal examples of multi-page apps using the Pages feature available in dash>=2.5.1
See the π Dash Documentation Multi-Page Apps and URL Support
π₯ Don't miss the video tutorials:
- Introducing Dash
pages
-- A better way to make multi-page apps` by Adam Schroeder and Chris Parmer. - Charming Data Videos by Adam Schroeder:
- Creating Multi Page Apps - Part I Getting Started
- Creating Multi Page Apps - Part II Sidebar and Layout Enhancements
This feature was developed in dash-labs. For background, see the thread on the Dash Community Forum.
I hope these examples help you get started exploring all the cool features in Pages. If you find this project helpful, please consider giving it a β
Example Apps
The best way to get started is to clone this repo and run the examples locally. See a brief description of each app below.
Other tutorials or examples using pages
:
-
Adding a Blog to your Dash app. See this Dash Community Forum post. It describes how to do this and includes this repo from @bradley-erickson.
-
See the Dash Webb Compare app live. This app shows the first images from the James Webb Space Telescope. Compare before and after images of Hubble vs Webb. The Github repo has 2 versions of the app using
pages
.- app_pages.py - Creates an multi-page app without using the
pages
folder. - app_pages_no_assets.py - This multi-page app uses images that are hosted on GitHub so it doesn't use either the
pages
or theassets
folder.
- app_pages.py - Creates an multi-page app without using the
Tips and Tricks
- Pretty print dash.page_registry - with the
print_registry()
function from dash-labs - How to use dcc.Link in Markdown - for high performance page navigation from a link in a dcc.Markdown component.
- Avoiding duplicate ids - Strategies for handling ids in a large multi-page app.
- Display loading screen when page_container is loading - Shows how to make the overall loading screen only display when there is a change to the
_pages_content
that involves a layout being changed and not changes within the layout.
This folder has a minimal overview of the basic pages features
, including:
- setting the default home page
- handling variables in the pathname
- updating the app title and description with a function
- handling variable in query strings
- setting redirects
- adding extra data to the
dash.page_registry
- customizing the
dash.page_registry
defaults - how images are added to meta tags
- adding pages without using the pages folder
The image below π is from the path_variables
page. Note that asset "inventory" and department "branch-1001" are passed from the pathname to the layout function and are displayed on the page.
This example shows how to use the relative_path
attribute in dash.page_registry
in deployment environments that use a pathname prefix.
It also shows use of dash.get_asset_url()
to get the correct path to the assets
folder from a file in the pages
folder.
relative_path
: The path withrequests_pathname_prefix
prefixed before it. Use this path when specifying local URL paths that will work in environments regardless of whatrequests_pathname_prefix
is. In some deployment environments, like Dash Enterprise,requests_pathname_prefix
is set to the application name, e.g.my-dash-app
. When working locally,requests_pathname_prefix
might be unset and so a relative URL like/page-2
can just be/page-2
. However, when the app is deployed to a URL like/my-dash-app
, thenrelative_path
will be/my-dash-app/page-2
.
Note the /app1/
pathname prefix in the url π
Example removed - please see #11 multi_page_store and #4 multi_page_cach_background_callbacks.
This example shows how to use caching and background callbacks in a multi-page app. The examples in the dash docs needed to be modified to make it possible to switch pages while background callbacks are running.
This example shows a small app with three pages with callbacks. Each page displays a figure. It uses dash-bootstrap-components with dbc.DropdownMenu
to display the links in a navbar.
This example is the multi_page_example1
app with HTTP Basic Auth from the
dash-auth` package.
Basic Auth section of the dash docs.
You will find two similar examples.
- multi_page_flask_login/ - original example
- multi_page_flask_login2/ - the new and improved version contributed by @jinnyzor. See this Dash Community Forum post for more information
For other authentication options see:
This app demonstrates how to create a sub-topics sidebar that is only used in certain pages. It shows how to use functions to access the dash.page_registry
from within the pages
folder after it's finished building.
For more details see also: https://dash.plotly.com/urls#dash-page-registry
This app shows more details on how the images are added to the meta tags. See also the Dash Documentation: https://dash.plotly.com/urls#meta-tags
For more info, please see the Dash Documentation: https://dash.plotly.com/urls#nested-pages This app demonstrates the case where you have nested folders with pages folder, like in the following:
- app.py
- pages
- chapter1
|-- page1.py
|-- page2.py
- chapter2
|-- page1.py
|-- page2.py
- home.py
It also demos how to add arbitrary data to the page_registry
. It adds icons to the page_registry
which are used when creating the links.
This app uses dash-mantine-components and dash-iconify libraries.
This app demonstrates passing variables to a page using query strings.
For more information see the Dash Documentation: https://dash.plotly.com/urls#query-strings.
You will also see how to use a dcc.Link
within a dcc.Markdown
This app shows how to share data between callbacks on different pages using a dcc.Store
component.
This app uses links in a table to navigate to a different page. It shows two tables:
- The
dash.DataTable
has links formatted using Markdown. - The html table uses
dcc.Link
. The advantage of the html table isdcc.Link
allow for the navigation to a new page without refreshing the page. The table is created with thedbc.Table.from_dataframe
function from thedash-bootstrap-components
library.
These examples show how to synchronize component values between pages.
You will find two example:
- multi_page_sync_components/ is a simple example which uses the same component on each page and sets
persistence=True
Thanks @nopria for the example! - multi_page_sync_components2/In some cases, the simple example won't work (ie component values updated in callbacks). Version 2 uses MultiplexerTransform from the dash-extensions library to update a
dcc.Store
component from multiple callbacks.
This example demonstrate a light and dark theme switch component from the dash-bootstrap-templates library.
See a live demo at Dash Bootstrap Theme Explorer The Theme Explorer app is also made with pages
π
For Dash Enterprise Customers, see: Dash Design Kit
With Dash Pages, the routing callback is under-the-hood, which reduces the amount of boilderplate code you need to write.
The best way to navigate is to use components such as the dcc.Link
or dbc.Button
. When the user clicks on these
links, it will navigate to the new page without refreshing the page, making the navigation very
fast. And the best part? No callback required! π
This works well when you have static links. However, at times, you may want to navigate based on an input field, dropdown, or clicking on a figure etc. There are two options:
- Update href of
dcc.Location
in a callback. Not recommended in Dash<2.9.2 because it refreshes the page. - Update the link in a callback. Best practice!
π New in dash 2.9.2 dcc.Location(refresh="callback-nav")
- navigate without refreshing the page. See examples below
See two versions of the same app. It shows both ways to navigate in a callback - by updating dcc.Location and by updating links.
The V2.9.2 version uses the "callback-nav" option in dcc.Location
so that the page does not refresh.
For more information see this community forum post.
Here are more examples. This one (best practice) is to update a link when a user clicks on a figure:
This option is available with dash>=2.9.2. It uses dcc.Location(refresh="callback-nav")
to navigate without refreshing the page.
img
There are some known issues with dcc.Location
. Here are some workarounds to avoid things like the browser crashing or the back button not
working:
- Only include a
dcc.Location
component if you need to update it in a callback. - Be sure to use only one
dcc.Location
component - do not use multiple. - Place the
dcc.Location
inapp.py
- do not put it in a file in thepages
folder. - Place the
dcc.Location
component as the first component in theapp.py
layout. It must be in the layout beforepage_container
which has the Pagesdcc.Location
component.
app.layout = html.Div(
[
dcc.Location(id="url", refresh="callback-nav"),
navbar, page_container,
],
)
When debugging a pages
app, it's very helpful to inspect the content of the dash.page_registry
.
print_registry()
is a handy utility that pretty-prints all or part of the dash.page_registry
dict.
Examples for print_registry()
from dash import Dash, html, register_page
# must use dash-labs>=1.1.0
from dash_labs import print_registry
app = Dash(__name__, use_pages=True)
register_page("another_home", layout=html.Div("We're home!"), path="/")
print_registry()
.... rest of your app
Will print to the console:
{'another_home': {'module': 'another_home',
'supplied_path': '/',
'path_template': None,
'path': '/',
'supplied_name': None,
'name': 'Another home',
'supplied_title': None,
'title': 'Another home',
'description': '',
'order': 0,
'supplied_order': None,
'supplied_layout': Div("We're home!"),
'image': None,
'supplied_image': None,
'image_url': None,
'redirect_from': None,
'layout': Div("We're home!")}}
Reference
print_registry(modules='ALL', exclude=None, include='ALL')
Params:
module
: (string or list) Default "ALL". Specifies which modules to print.exclude
: (string or list) Default None. Specifies which of the page's parameter(s) to exclude.include
: (string or list) Default "ALL". Prints only the parameters that are specified.
Examples:
print_registry()
Will print the entire content of dash.page_registry. If called from a file in the pages folderdash.page_registry
may not be complete.print_registry("pages.home")
will print only one module, in this case, thepages.home
moduleprint_registry(__name__)
will print the current module. When called from app.py it will print all modules.print_registry(["pages.home", "pages.archive"])
Will print the modules in the list.print_registry(exclude="layout")
will print info for all the modules, but will exclude the "layout" attributeprint_registry(include=["path", "name"]
will print only the "path" and "name" attributes for all modulesprint_registry(include=None)
prints the keys (module names) only
Did you know it's possible to use dcc.Link in dcc.Markdown
?
The advantage of using dcc.Link
to navigate between pages of a multi-page app is that when you click on the link it updates the pathname without refreshing the page -- which makes browsing really fast. π
Here's how:
dcc.Markdown( "This is text <dccLink href='page1/news' children='Page 1' /> more text", dangerously_allow_html=True)
For comparison, here is a regular Markdown link syntax:
dcc.Markdown( "This is text [Page 1](/page1/news) more text")
See multi_page_query_strings/ for an example. For more examples including how to format the link title with Markdown syntax or use an image get the gist.
All ids in the entire app must be unique, otherwise callbacks may not fire. Here are some tips to ensure that all ids in the app are unique:
3a. From this forum post as recommended by @chriddyp:
What Iβve done in big projects is to create an id function that creates the prefix automatically. This is easier with pages as each component tree is in its own layout so you can use
__name__
as the prefix.So youβd write something like:
utils.py
def id(name, localid):
return f"{name.replace('_', '-').replace('.py', '').replace('/', '')}-{localid}"
pages/historical_analysis.py
from utils import id
layout = html.Div(id=id(__name__, 'parent-div'))
3b. From this video by arjancodes
Define ids in module. It makes them easier to access, maintain, and reduces typos. See this used in multi_page_long_callback
for example:
ids.py
PAGE1_BUTTON = "page1-button"
PAGE1_GRAPH = "page1-graph"
page1.py
import ids
html.Button("button", id=ids.PAGE1_BUTTON)
dcc.Graph(ids.PAGE1.GRAPH)
@callback(
Output(ids.PAGE1_GRAPH, "figure"),
Input(ids.PAGE1_BUTTON, "n_clicks")
)
Shows how to make the overall loading screen only display when there is a change to the _pages_content
that involves a layout being changed and not changes within the layout. See the post on the Dash Community Forum. Thanks @BSd3v for this example!