📣 Dash Labs 1.0.0: Dash `pages` - An easier way to make multi-page apps

I’m pleased to announce that "Dash pages/" preview is now available in Dash Labs :confetti_ball:

This new feature simplifies creating multi-page Dash apps. See the initial announcement and forum discussion. We moved this to Dash Labs to make it easier for you to try it out (pip install dash-labs==1.0.0) and give feedback.

Background

Dash Labs is used to develop new features for future releases of Dash. You can see the community discussion on the progress prior to Dash 2.0 in the initial announcement and in subsequent releases.

Here are some features that started in Dash Labs and were added to Dash >= 2.0.

The documentation for these initial projects in dash-labs is still available in the current version of dash-labs, but the code is not. The code for the older versions are available in dash-labs v0.4.0. (pip install dash-labs==0.4.0)

Dash Labs V1.0.0

Dash Labs is now set up to develop new features starting with dash>=2.0 and dash-bootstrap-components>=1.0. The first new project we’ve added is the Dash pages/ feature. We’ll be adding more new projects in the coming months.

We received a lot of great feedback from the community in our previous announcement (thank you!) and opened up issues for those requests.

Give it a try! See links to the new documentation below. We encourage you take the new features for a spin and to join the discussion, raise issues, make pull requests, and take an active role in shaping the future of Dash.

Dash pages/ Documentation

Quickstart:

pip install dash-labs==1.0.0

app.py

import dash
import dash_labs as dl
import dash_bootstrap_components as dbc


app = dash.Dash(
    __name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP]
)

navbar = dbc.NavbarSimple([
        dbc.NavItem(dbc.NavLink(page['name'], href=page['path']))
        for page in dash.page_registry.values()
        if page["module"] != "pages.not_found_404"
], brand='Dash App')

app.layout = dbc.Container(
    [navbar, dl.plugins.page_container],
)

if __name__ == "__main__":
    app.run_server(debug=True)

pages/home.py

import dash
from dash import dcc, html, Input, Output, callback

dash.register_page(__name__, path="/")

layout = html.Div([
        html.H1('Home Page')
])

pages/historical_analysis.py

import dash
from dash import dcc, html, Input, Output, callback

dash.register_page(__name__, path="/historical-analysis")

layout = html.Div([
    html.H1('Historical Analysis Page')
])

In-Depth Guides


We welcome all pull requests to help improve the dash-labs documentation, even as minor as fixing a typo or writing a better sentence. If you would like to contribute to Dash, but are not sure how, this is a great place to start.

Our goal is to create high quality documentation that can be added directly to the official Dash documentation when new features are added to Dash. You can help – even if you’re new to Dash. Try following the instructions to make a multi-page app. Did we miss any steps? Is anything unclear? If so, let us know either here or in the GitHub issue tracker.

8 Likes

@chriddyp In my current application, I use the existing multipage application format as suggested by the docs and would love to convert over to this, but I’m also simultaneously looking at Flask-Security-Too for authentication. I had previously intended to verify the user’s role/login status within the callback in the index.py file that handles page routing. Basically, if the user was authenticated, then serve them the page layout and if they were not logged in, then return them to the login splash screen. Is there any way to incorporate that functionality into this new pages/ structure or would this functionality require that I continue to use the previous url routing callback? Thanks!

:mega: I’m pleased to announce the release of Dash Labs V1.0.1

pip install dash-labs -U

This release includes two bug fixes:

  • #59 Fixed bug that prevented order prop from changing the order of the modules in dash.page_registry
  • #55 Fixed bug that prevented multipage apps from working in windows

Big thanks to @johnkangw for reporting the Windows bug and for doing the pull request to fix it! :medal_sports:

4 Likes

@AnnMarieW Thanks for the kudos and appreciate you walking me through the whole pull request process! I’ve shown our proof of concept to our VPs and they really like it. It looks like Dash Enterprise maybe in the future for us…

2 Likes

I am so happy this feature is being implemented, but I have one question.

Is it possible to modularize each page? While I can deal with having enormous files, I prefer to breakup my page layout, the callbacks, and the call to my api in separate files. I tried this out using the below structure, but ran into the following error
page["layout"]() if callable(page["layout"]) else page["layout"]

This is how I’d imagine the folder structure to look.

pages
|- some_page
  |- __init__.py
  |- layout.py
  |- callbacks.py
  |- data.py
|- home.py

on page1.py

from tasks import task1

on page2.py

from tasks import task2

Error:

ImportError: cannot import name 'task2' from partially initialized module 'tasks' (most likely due to a circular import) (E:\test\tasks.py)

Hi @Sylvos

Thanks for reporting. I think I know why this is happening, but I’m not able to reproduce the error. Could you provide a more complete example?

Hi @raptorbrad

Welcome to the Dash community - and thanks for trying this new feature :slight_smile:

I’ve tried doing it this way, and it worked for me as long as dash.register_page(__name__) is only included in the files with the layout (ie not in callbacks.py and data.py)

Can you provide an example the reproduces the error you are seeing?

2 Likes

Thanks for the reply,

I was initially trying to register the page in the init file after importing the layout I defined in layout.py. When I registered the page within layout.py it seems to work just fine now.

2 Likes

I modified the import method, it seems that no error is reported

import tasks
print(tasks.task1)

Another question, is my custom route unavailable?

import app
server = app.server
@server.route("/test", methods=["GET", "POST"])
def test():
    pass

Hi @Sylvos Can you say more about your use-case? Have you tried it and are you getting any errors?

pip install dash-labs -U
This worked for me.

1 Like

Hi,

I am trying to build a multi-page app with dash labs that use the volume slicer. I want to create volume slicer objects on a page. Does anyone know whether it is possible or not? I don’t know how to include the dash application instance in the volume slicer class object.

Many Thanks!

Hi @iromi

This should be possible. Could you please provide a minimal working example of the volume slicer you are trying to use?

1 Like

Hi @AnnMarieW

Please find the attached sample code.

import dash
from dash import html
import dash_bootstrap_components as dbc
from dash import dcc
from dash_slicer import VolumeSlicer
from pymatreader import read_mat

app = dash.Dash(__name__, update_title=None, external_stylesheets=[dbc.themes.BOOTSTRAP])
server = app.server

# Read volume
path = "D:\data\Patient1.mat"

data = read_mat(path)
vol = data['ct_image']

# Create slicer objects
slicer0 = VolumeSlicer(app, vol, axis=0, color="#00ff99", scene_id="ct_bone")

app.layout = html.Div(

    children=[
        dcc.Store(id={"context": "app", "scene": slicer0.scene_id, "name": "setpos"}),

        # Graph bar to display all the axes
        dbc.Card(
            dbc.CardBody(
                [
                    dbc.Row(
                        [
                            dbc.Col(
                                html.Div(
                                    [
                                        html.Center(html.H1("Transversal")),
                                        slicer0.graph,
                                        html.Br(),
                                        slicer0.slider,
                                        *slicer0.stores,
                                 ]
                                ),

                            ),


                        ],
                        style={"display": "grid", "gridTemplateColumns": "33% 33% 33%"}
                    ),

                ],
            ),

        ),

        html.Br(),
    ],
)

if __name__ == "__main__":
    # Note: dev_tools_props_check negatively affects the performance of VolumeSlicer
    app.run_server(debug=True, dev_tools_props_check=False)

This works fine when I define the vol(3d image) as in the code. But my requirement is to get the vol from the patient files that the user uploads. I can’t figure out how to assign the output of the callback as the parameters to the object.
I am stuck here for weeks.

Many Thanks