Examples of multi-page apps with Dash Pages

I made this GitHub repo to help people get started making multi-page apps with the new Dash Pages feature available in Dash 2.5.1. It has lots of examples you can use to explore some of the great new features when using pages. You will also find other handy tips and tricks as well.

If you have ideas for other examples you would like to see, please open and issue. Pull requests are welcome too!



Dash pages multi-page app demos

This repo contains minimal examples of multi-page apps using the pages feature available in dash>=2.5.1

See the :new: Dash Documentation Multi-Page Apps and URL Support

:movie_camera: Don’t miss the video tutorials:

This feature was developed in dash-labs. For background, see the thread on the Dash Community Forum.
If you have a multi-page app using the pages plugin from dash-labs, see the post on how to migrate to dash>=2.5.1.

Example Apps

The examples are listed by their folder name.

  1. multi_page_basics/ - minimal overview of basic pages features.
  2. multi_page_pathname_prefix/ - overview using a pathname prefix.
  3. multi_page_cache/ - sharing data between pages with caching.
  4. multi_page_example1/ - 3 page app with header navbar, graphs and callbacks. Uses dash-bootstrap-components.
  5. multi_page_flask_login/ - uses flask-login to secure one page of a multi-page app.
  6. multi_page_layout_functions/ - uses a function to access dash.page_registry from within the pages folder to build a sidebar.
  7. multi_page_meta_tags/ - how images are used in meta tags when sharing the app on social media.
  8. multi_page_nested_folder/ - creates a sidebar from a sub folder in the pages folder and adds arbitrary data to dash.page_registry. Uses dash-mantine-components
  9. multi_page_query_strings/ - passes variables to the layout function from the url query string.
  10. multi_page_store/ - sharing data between pages with a dcc.Store.
  11. multi_page_table_links/ - uses links in a DataTable and an html table for navigation and passes variables from the pathname to the page layout function.
  12. multi_page_sync_components/ - syncs components between pages using MultiplexerTransform from dash-extensions to update a dcc.Store from multiple callbacks.
  13. multi_page_theme_switch/ - demos a light and dark theme switch component from the dash-bootstrap-templates library.
  14. multi_page_update_url_in_callback/ - page navigation via callback rather than a user clicking on a link.

Other tutorials or examples using pages:

  1. 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.

  2. 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 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 the assets folder.

Tips and Tricks

  1. Pretty print dash.page_registry - with the print_registry() function from dash-labs
  2. How to use dcc.Link in Markdown - for high performance page navigation from a link in a dcc.Markdown component.


Demos

1. multi_page_basics/

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 :point_down: 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.

basics


2. multi_page_pathname_prefix/

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 with requests_pathname_prefix prefixed before it.
    Use this path when specifying local URL paths that will work
    in environments regardless of what requests_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, then
    relative_path will be /my-dash-app/page-2.

Note the /app1/ pathname prefix in the url :point_down:

pathname_prefix


3. multi_page_cache/

This example shows how to share data between pages of a multi-page app using caching.

The easiest way to share data between callbacks is to use dcc.Store(). See also example #10 multi-page-store. However, if you have large data, then you may want to use caching as described in example 3 and 4 in the Dash tutorial sharing data between callbacks.

This example also demonstrates the use of the new dash.get_app() function that can be used to access the app object from modules within the pages folder without running into the circular imports issue.
cache


4. multi_page_example1/

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.

Example1

5. multi_page_flask_login/

This shows a minimal example of flask-login to secure one of the pages of a multi-page app.
This code is adapted for pages based on Nader Elshehabi’s article and github repo.

For other authentication options see:

flask__login


6. multi_page_layout_functions/

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: Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly

7. multi_page_meta_tags/

This app shows more details on how the images are added to the meta tags.
See also the Dash Documentation: Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly

8. multi_page_nested_folder/

For more info, please see the Dash Documentation: Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly
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.
nested_folders


9. multi_page_query_strings/

This app demonstrates passing variables to a page using query strings.
For more information see the Dash Documentation: Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly

query_strings


10. multi_page_store/

This app shows how to share data between callbacks on different pages using a dcc.Store component.

share_data_between_pages



11. multi_page_table_links/

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 is dcc.Link allow for the navigation to a new page without refreshing the page. The table is created with the dbc.Table.from_dataframe function from the dash-bootstrap-components library.

table_links


12 multi_page_sync_components/

This example shows how to synchronize component values between pages. It uses MultiplexerTransform from the dash-extensions library to update a dcc.Store component from multiple callbacks.

sync

13. multi_page_theme_switch/

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 :tada:

For Dash Enterprise Customers, see: Dash Design Kit

theme_switch


14. multi_page_update_url_in_callback/

This example shows how to update the url in a callback. It passes the value of the dcc.Input to the layout of a different page as a path variable.
It also demonstrates using urllib.parse.unquote to get decoded strings from the url.

update_url_in_callback



Tips and Tricks

1. print_registry() from dash-labs>-1.1.0

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 folder dash.page_registry may not be complete.
  • print_registry("pages.home") will print only one module, in this case, the pages.home module
  • print_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” attribute
  • print_registry(include=["path", "name"] will print only the “path” and “name” attributes for all modules
  • print_registry(include=None) prints the keys (module names) only

2. :tada: Use dcc.Link in dcc.Markdown

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. :rocket:

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")

For more examples including how to format the link title with Markdown syntax or use an image get the gist.

4 Likes

Thanks for the shoutout, @AnnMarieW , and thank you for sharing all these code examples to help people further explore Pages.

1 Like

This is a really great thread, thanks for sharing.
I’ve just finished a big project with multipage and this would’ve come in handy.
Maybe I’ll refactor said project just to get acquainted with the new way of making multi-page apps.

1 Like

Are layout() functions for all pages loaded before assets sent to client? I add sleep(60) to page-2 layout(), visit page-1 in the browser and it takes 1 minute to load. Trying to wrap my head around Pages with pathname callbacks and was noticing functions executing that weren’t be called

1 Like

@crachel

Yes, all the layout functions are called after the modules are loaded from the pages folder. Pages automatically validates the app as described in the docs here. The layout functions must be called in order to validate.

While this is convenient and can prevent some bugs, perhaps it should be optional. As you point out, it can make the app slower to start if some layout functions are slow.

I opened a issue #2172 - thanks for asking the question.

Thank you. I think it has to be otherwise the GET parameters in the layout() functions can’t be used for database queries

Also, I see the flask-login example but it’s pretty easy to add full feature auth to dash apps using bearer tokens and hosted UI’s, I just need State from the client. No problem, with Dash Pages, I can have a dcc.Location callback that includes the state of a dcc.Store. This callback, as long as the output (in my case a wrapper around dash.page_container) is present in the initial layout, is very fast and happens in parallel to Dash Pages doing its thing. However, since the callbacks are independent, the dash_renderer may let the dcc.Location callback go first, or the Dash Pages layout(), therefore “payload” potentially gets sent to the client, even if it’s briefly before I can redirect them to login or clear the wrapper’s children. It would be nice if there was some sort of user state that could be passed to the layouts() to “short circuit” requests before they actually happen

Is there any example with best practices for handling component id duplication across pages? We have ~50 pages written by ~15 people and managing duplicate id is a bit of a headache. Is the best practice just requiring each page to carefully use an id prefix for all components or does pages have new support (I haven’t seen this addressed in any docs yet). Would be awesome if pages could namespace ids to each page (perhaps by auto adding a page prefix), not sure what unintended side effects that may have though.

2 Likes

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'))
2 Likes