# Structuring a multi-tab app

**URL:** <https://community.plotly.com/t/structuring-a-multi-tab-app/13331>\
**Category:** Dash Python\
**Tags:** show-and-tell\
**Created:** [September 5, 2018, 8:54pm UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331 "2018-09-05T20:54:25Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![austin](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/austin/32/3328_2.png) [@austin](https://community.plotly.com/u/austin)\
**Post date:** [September 5, 2018, 8:54pm UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/1 "2018-09-05T20:54:25Z")

</div>

I’ve been following the [documentation for dcc.Tabs](https://dash.plot.ly/dash-core-components/tabs) but I wanted to structure my app across multiple files rather than a single (bloated) file. Following the example listed in the [URLs and Multi-Page App Tutorial](https://dash.plot.ly/urls), I was able to build a simple but successful app where the layouts for each tab are saved in separate files. Note that I had to place all my callbacks in the primary app (main.py), and not in the secondary files.

# file structure:

main.py  
tabs folder  
— tab\_1.py  
— tab\_2.py

### main.py

```auto
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
from tabs import tab_1
from tabs import tab_2

app = dash.Dash()

app.config['suppress_callback_exceptions'] = True

app.layout = html.Div([
    html.H1('Dash Tabs component demo'),
    dcc.Tabs(id="tabs-example", value='tab-1-example', children=[
        dcc.Tab(label='Tab One', value='tab-1-example'),
        dcc.Tab(label='Tab Two', value='tab-2-example'),
    ]),
    html.Div(id='tabs-content-example')
])

@app.callback(Output('tabs-content-example', 'children'),
              [Input('tabs-example', 'value')])
def render_content(tab):
    if tab == 'tab-1-example':
        return tab_1.tab_1_layout
    elif tab == 'tab-2-example':
        return tab_2.tab_2_layout

# Tab 1 callback
@app.callback(dash.dependencies.Output('page-1-content', 'children'),
              [dash.dependencies.Input('page-1-dropdown', 'value')])
def page_1_dropdown(value):
    return 'You have selected "{}"'.format(value)

# Tab 2 callback
@app.callback(Output('page-2-content', 'children'),
              [Input('page-2-radios', 'value')])
def page_2_radios(value):
    return 'You have selected "{}"'.format(value)

app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})

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

```

### tab\_1.py

```auto
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

tab_1_layout = html.Div([
    html.H1('Page 1'),
    dcc.Dropdown(
        id='page-1-dropdown',
        options=[{'label': i, 'value': i} for i in ['LA', 'NYC', 'MTL']],
        value='LA'
    ),
    html.Div(id='page-1-content')
])

```

### tab\_2.py

```auto
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

tab_2_layout = html.Div([
    html.H1('Page 2'),
    dcc.RadioItems(
        id='page-2-radios',
        options=[{'label': i, 'value': i} for i in ['Orange', 'Blue', 'Red']],
        value='Orange'
    ),
    html.Div(id='page-2-content')
])
``
```

---

<div class="post-metadata">

**Author:** ![helmoai](https://avatars.discourse-cdn.com/v4/letter/h/94ad74/32.png) [@helmoai](https://community.plotly.com/u/helmoai)\
**Post date:** [September 19, 2018, 12:40pm UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/2 "2018-09-19T12:40:50Z")

</div>

I have updated your main.py file to work with dash-core-components==0.13.0rc5

```

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
from tabs import tab_1
from tabs import tab_2

app = dash.Dash()

app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    html.H1('Dash Tabs component demo'),
        dcc.Tabs(
            tabs=[
                {'label': 'Tab One', 'value': 'tab-1-example'},
                {'label': 'Tab Two', 'value': 'tab-2-example'}
            ],
            value='tab-1-example',
            id='tabs-example'
        ),
    html.Div(id='tabs-content-example')
])

@app.callback(Output('tabs-content-example', 'children'),
              [Input('tabs-example', 'value')])
def render_content(tab):
    if tab == 'tab-1-example':
        return tab_1.tab_1_layout
    elif tab == 'tab-2-example':
        return tab_2.tab_2_layout

# Tab 1 callback
@app.callback(dash.dependencies.Output('page-1-content', 'children'),
              [dash.dependencies.Input('page-1-dropdown', 'value')])
def page_1_dropdown(value):
    return 'You have selected "{}"'.format(value)

# Tab 2 callback
@app.callback(Output('page-2-content', 'children'),
              [Input('page-2-radios', 'value')])
def page_2_radios(value):
    return 'You have selected "{}"'.format(value)

app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})

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

```

---

<div class="post-metadata">

**Author:** ![Kaofu](https://avatars.discourse-cdn.com/v4/letter/k/9d8465/32.png) [@Kaofu](https://community.plotly.com/u/Kaofu)\
**Post date:** [March 15, 2019, 12:35pm UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/3 "2019-03-15T12:35:17Z")

</div>

Thank you for sharing this example! Very helpful.

As far as I understand, in order to define the callbacks outside of the main app (either in the tab file or a separate file just for callbacks), you need to have a separate app.py file that you import from your main app file so instead of _app = dash.Dash()_ you have _from app import app_

It is explained in the bottom of the page [URLs and Multi-Page App Tutorial](https://dash.plot.ly/urls) that you quoted, but maybe was not when you created this example. (So I thought I just quote it here for reference)

> It is worth noting that in both of these project structures, the Dash instance is defined in a separate `app.py` , while the entry point for running the app is `index.py` . This separation is required to avoid circular imports: the files containing the callback definitions require access to the Dash `app` instance however if this were imported from `index.py` , the initial loading of `index.py` would ultimately require itself to be already imported, which cannot be satisfied.

And the app.py file is simply just something like this

```auto
import dash

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash( __name__ , external_stylesheets=external_stylesheets)
server = app.server
app.config.suppress_callback_exceptions = True

```

---

<div class="post-metadata">

**Author:** ![austin](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/austin/32/3328_2.png) [@austin](https://community.plotly.com/u/austin)\
**Post date:** [April 30, 2019, 11:57am UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/4 "2019-04-30T11:57:24Z")

</div>

Thanks, @Kaofu. I had not seen this before. If the entry point for running the app is now `index.py`, how does that affect the naming conventions used in deployment? For example, in the user guide for [deploying on Heroku](https://dash.plot.ly/deployment), it specifies that the app must be named `app.py` and then called as such in `Procfile`:

```auto
web: gunicorn app:server
(Note that app refers to the filename app.py. server refers to the variable server inside that file).

```

Thanks!

---

<div class="post-metadata">

**Author:** ![user123](https://avatars.discourse-cdn.com/v4/letter/u/ed655f/32.png) [@user123](https://community.plotly.com/u/user123)\
**Post date:** [December 5, 2019, 11:13pm UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/5 "2019-12-05T23:13:18Z")

</div>

I was wondering in case of a multi-tab app like this one, how is layout managed for a login page ? On the login page, we don’t want Tabs appearing. However, Tabs are part of the app.layout. So as the code as is, will show Tabs on the login page. How can Tabs be removed from the login page ?

One hack I can think of is to hide the Tabs on login page (‘display’:‘none’). However, that will still leave the space occupied by Tabs as empty space. Any better solution ?

May be make Tabs part of Page 1 & Page 2 but not part of the app.layout ?

---

<div class="post-metadata">

**Author:** ![vanslerry](https://avatars.discourse-cdn.com/v4/letter/v/54ee81/32.png) [@vanslerry](https://community.plotly.com/u/vanslerry)\
**Post date:** [February 26, 2020, 1:09am UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/6 "2020-02-26T01:09:21Z")

</div>

Hi @austin, I am a newbie to python and dash plotly. I tried using your scripts tp create a simple mutli tab dashboard, but i am getting an error

ImportError: cannot import name ‘tab\_1’

I have followed your folder structure. Please do you know that the problem might be?

---

<div class="post-metadata">

**Author:** ![ajeytiwary](https://avatars.discourse-cdn.com/v4/letter/a/22d042/32.png) [@ajeytiwary](https://community.plotly.com/u/ajeytiwary)\
**Post date:** [January 19, 2022, 10:52am UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/7 "2022-01-19T10:52:42Z")

</div>

This doesn’t work anymore in the

> dcc.Tabs component (version 2.0.0)

I get an error

```auto
"tabs-example" received an unexpected keyword argument: `tabs`

```

is there a way to use this, in the version 2.0.0?

---

<div class="post-metadata">

**Author:** ![jlfsjunior](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/jlfsjunior/32/16061_2.png) [@jlfsjunior](https://community.plotly.com/u/jlfsjunior)\
**Post date:** [January 19, 2022, 11:37am UTC](https://community.plotly.com/t/structuring-a-multi-tab-app/13331/8 "2022-01-19T11:37:26Z")

</div>

It should be just a matter of replacing the `tabs` keyword argument in `dcc.Tabs` for `children`. You can find an updated example [here](https://dash.plotly.com/dash-core-components/tabs#method-1.-content-as-callback).
