Styling the DataTable

Default Styles

By default, the DataTable has grey headers and borders around each cell. It resembles a spreadsheet and the headers are clearly defined.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)]

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Column Alignment

When displaying numerical data, it's a good practice to use monospaced fonts, to right-align the data, and to provide the same number of decimals throughout the column.

To learn about formatting numbers and dates, see Typing.

For textual data, left-aligning the text is usually easier to read.

In both cases, the column headers should have the same alignment as the cell content.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_cell=Dict("textAlign" =>  "left"),
    style_cell_conditional=[
        Dict(
            "if" =>  Dict("column_id" =>  "Region"),
            "textAlign" =>  "left"
        )
    ]

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Styling the Table as a List

The gridded view is a good default view for an editable table as it looks and feels like a spreadsheet. If your table isn't editable, then in many cases it can look cleaner without the vertical grid lines.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_cell_conditional=[
        Dict(
            "if" =>  Dict("column_id" =>  c),
            "textAlign" =>  "left"
        ) for c in ["Date", "Region"]
    ],

    style_as_list_view=true,

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

List Style with Minimal Headers

In some contexts, the grey background can look a little heavy. You can lighten this up by giving it a white background and a bold text.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_as_list_view=true,
    style_cell=Dict("padding" =>  "5px"),
    style_header=Dict(
        "backgroundColor" =>  "white",
        "fontWeight" =>  "bold"
    ),
    style_cell_conditional=[
        Dict(
            "if" =>  Dict("column_id" =>  c),
            "textAlign" =>  "left"
        ) for c in ["Date", "Region"]
    ],

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Striped Rows

When you're viewing datasets where you need to compare values within individual rows, it can sometimes be helpful to give the rows alternating background colors. We recommend using colors that are faded so as to not attract too much attention to the stripes.

Sign up for Dash Club → Two free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture. Join now.

Notice the three different groups you can style: "cell" is the whole table, "header" is just the header rows, and "data" is just the data rows. To use even/odd or other styling based on row_index you must use style_data_conditional.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_cell_conditional=[
        Dict(
            "if" =>  Dict("column_id" =>  c),
            "textAlign" =>  "left"
        ) for c in ["Date", "Region"]
    ],
    style_data_conditional=[
        Dict(
            "if" =>  Dict("row_index" =>  "odd"),
            "backgroundColor" =>  "rgb(248, 248, 248)"
        )
    ],
    style_header=Dict(
        "backgroundColor" =>  "rgb(230, 230, 230)",
        "fontWeight" =>  "bold"
    )

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Multi-Headers

Multi-headers are natively supported in the DataTable. Just set name inside columns as a list of strings instead of a single string and toggle merge_duplicate_headers=True. DataTable will check the neighbors of each header row and, if they match, will merge them into a single cell automatically.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
  columns=[
    Dict("name" =>  ["", "Year"], "id" =>  "year"),
    Dict("name" =>  ["City", "Montreal"], "id" =>  "montreal"),
    Dict("name" =>  ["City", "Toronto"], "id" =>  "toronto"),
    Dict("name" =>  ["City", "Ottawa"], "id" =>  "ottawa"),
    Dict("name" =>  ["City", "Vancouver"], "id" =>  "vancouver"),
    Dict("name" =>  ["Climate", "Temperature"], "id" =>  "temp"),
    Dict("name" =>  ["Climate", "Humidity"], "id" =>  "humidity"),
],
data=[
    Dict(
        "year" =>  i,
        "montreal" =>  i * 10,
        "toronto" =>  i * 100,
        "ottawa" =>  i * -1,
        "vancouver" =>  i * -10,
        "temp" =>  i * -100,
        "humidity" =>  i * 5,
    )
    for i in 1:10
],
merge_duplicate_headers=true,

)

run_server(app, "0.0.0.0", debug=true)
City
Climate
Year
Montreal
Toronto
Ottawa
Vancouver
Temperature
Humidity
0
0
0
0
0
0
0
1
10
100
-1
-10
-100
5
2
20
200
-2
-20
-200
10
3
30
300
-3
-30
-300
15
4
40
400
-4
-40
-400
20
5
50
500
-5
-50
-500
25
6
60
600
-6
-60
-600
30
7
70
700
-7
-70
-700
35
8
80
800
-8
-80
-800
40
9
90
900
-9
-90
-900
45

Dark Theme with Cells

You have full control over all of the elements in the table. If you are viewing your table in an app with a dark background, you can provide inverted background and font colors.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_header=Dict("backgroundColor" =>  "rgb(30, 30, 30)"),
    style_cell=Dict(
        "backgroundColor" =>  "rgb(50, 50, 50)",
        "color" =>  "white"
    ),

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Conditional Formatting

See the new conditional formatting chapter.

Styles Priority

There is a specific order of priority for the style_* properties. If there are multiple style_* props, the one with higher priority will take precedence. Within each prop, rules for higher indices will be prioritized over those for lower indices. Previously applied styles of equal priority win over later ones (applied top to bottom, left to right).

These are the priorities of style_* props, in decreasing order:

  1. style_data_conditional
  2. style_data
  3. style_filter_conditional
  4. style_filter
  5. style_header_conditional
  6. style_header
  7. style_cell_conditional
  8. style_cell
using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_header=Dict( "border" =>  "1px solid black" ),
    style_cell=Dict( "border" =>  "1px solid grey" ),

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Adding Borders

Customize the table borders by adding border to style_* props.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c) for c in names(df)],
    style_data=Dict( "border" =>  "1px solid blue" ),
    style_header=Dict( "border" =>  "1px solid pink" ),

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15

Styling Editable Columns

Editable columns can be styled using column_editable in style_header_conditional, style_filter_conditional, and style_data_conditional props.

using Dash 
using OrderedCollections, DataFrames   

data = OrderedDict(
      "Date" => ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"],
      "Region" => ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"],
      "Temperature" => [1, -20, 3.512, 4, 10423, -441.2],
      "Humidity" => [10, 20, 30, 40, 50, 60],
      "Pressure" => [2, 10924, 3912, -10, 3591.2, 15],
)

df = DataFrame(data) 

app = dash()

app.layout = dash_datatable(
    data = map(eachrow(df)) do r
      Dict(names(r) .=> values(r))
    end,
    columns=[Dict("name" =>c, "id" => c, "editable" => (c == "Humidity")) for c in names(df)],
    style_data_conditional=[Dict(
        "if" =>  Dict("column_editable" =>  false),
        "backgroundColor" =>  "rgb(30, 30, 30)",
        "color" =>  "white"
    )],
    style_header_conditional=[Dict(
        "if" =>  Dict("column_editable" =>  false),
        "backgroundColor" =>  "rgb(30, 30, 30)",
        "color" =>  "white"
    )],

)

run_server(app, "0.0.0.0", debug=true)
Date
Region
Temperature
Humidity
Pressure
2015-01-01
Montreal
1
10
2
2015-10-24
Toronto
-20
20
10924
2016-05-10
New York City
3.512
30
3912
2017-01-10
Miami
4
40
-10
2018-05-10
San Francisco
10423
50
3591.2
2018-08-15
London
-441.2
60
15