Best choice to work with multi-pages or layouts etc

Hello everyone, good afternoon.

I have a question regarding the architecture and use of Python Dash, and I’d appreciate any insights or discussion on the topic.

I’m currently building a web application, and based on the documentation, there are numerous ways to work with multi-pages, multi-layouts, etc.

Given this, what is the best structure or approach to use the library in order to develop a well-structured and scalable application? Should I use the multi-pages module? Alternate between layouts? Or is there a specific logical architecture that works best?

In my specific case, I currently have three files: a login page, a home page, and another distinct page. However, my code is becoming a mess because there are so many ways to implement things when you’re not fully familiar with the best practices, and now my code has turned into a Frankenstein. Every time I try to reorganize it, I discover another way to improve it, which forces me to refactor the entire codebase.

Any guidance or advice would be greatly appreciated!

Hi Kurono!

I can recommend something like:

project_root/
├── app/
│ ├── api/
│ │ ├── config/
│ │ │ ├── celery_setup.py
│ │ │ ├── db_setup.py
│ │ │ └── redis_setup.py
│ │ ├── models/
│ │ └── queries/
│ │ ├── q1.sql
│ │ ├── q2.sql
│ ├── assets/
│ ├── authentication/
│ ├── global_components/
│ ├── pages/
│ │ ├── login/
│ │ │ ├── components/
│ │ │ │ └── login_form.py
│ │ │ └── page.py
│ │ ├── home/
│ │ │ ├── components/
│ │ │ │ └── dashboard.py
│ │ │ └── page.py
│ │ └── custom_page/
│ │ ├── components/
│ │ │ └── custom_component.py
│ │ |── page.py
│ │ └── api.py
│ ├── utils/
│ ├── init.py
│ ├── app.py
│ ├── dev_app.py
│ └── theme.py
├── tests/
├── .dockerignore
├── .env
├── .gitignore
├── dev.worker.Dockerfile
├── dev.Dockerfile
├── docker-compose.yml
├── Dockerfile
├── dependencies.lock
├── project_config.toml
├── readme.md
└── requirements.txt

Depending on your preferences you can create a callbacks.py file in the pages directories or construct the callbacks in class Components.
Either way, I would recommend using ID classes to structure and pass Ids to callbacks. This makes also a bit easier when composing bigger components.

class BigComponent(html.Div):
    class ids:
        id_1 = 'id1'
        id_2 = 'id2'
        id_3 = lambda idx: {'type': 'pm', 'index': idx}

    def __init__(self, *args, **kwargs):
        super().__init__(
            children=[
                html.Div(id=self.ids.id_1),
                html.Button('Button', id=self.ids.id_2)
            ],
            *args, **kwargs
        )

@callback(
    Output(BigComponent.ids.id_1, 'children'),
    Input(BigComponent.ids.id_2, 'n_clicks')
)

def update(n_clicks):
    if not n_clicks:
        raise PreventUpdate
    
    patch = Patch()
    patch.append(
        html.Div(id=BigComponent.ids.id_3(n_clicks))
    )
    return patch

@callback(
    Output(BigComponent.ids.id_3(MATCH), 'children'),
    Input(BigComponent.ids.id_3(MATCH), 'id')
)

def update(present):
    return 'Pattern match update'

Maybe this helps!

See also: https://dash.plotly.com/all-in-one-components