Good looking Table for a Streamlit application... is anyone still using aggrid?

Hi, I am a long time fan of Streamlit.
Recently I have been building my company’s main internal data application using Streamlit, highlighting the benefits of using Python and being able to ship faster.
Still, when I am presenting the application to other stakeholders - like board members - the application is just “not good looking”, especially the tables.

Having a good looking table like “Aggrid” would solve 99% of the “ugliness” of streamlit data_editor / dataframe, but aggrid is simply buggy and not maintained.

Has anyone dealt with such scenario? How did you make the tables “beautiful”?

Note that I know the purpose of Streamlit, which is to make data applications easier to build, but it would make my life 100% better if I could shift the focus of my data application to the actual analysis, and not the “poor” design of dataframes.

Thanks a lot, João

Can you post a sample table that is good-looking?

I tried showing the datatables in streamlit, does this looks good?

1 Like

Hi Ferdy, thanks for the reply!
Yes it does, how did you do it?

I am thinking on using something like tailwind css to create some table component. The data editor seems a little raw in general.
this is a very beautiful example. Having something like this on streamlit would be incredible.

First we need an html template that can take a url that contains a csv.

table_template.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Beautiful Table with CSV Data</title>

        <!-- Include DataTables CSS and JS -->
        <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.25/css/jquery.dataTables.css">
        <script type="text/javascript" charset="utf8" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
        <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.js"></script>
    </head>
    <body>

        <!-- Table container -->
        <div style="width: 80%; margin: 20px 0 0 0; float: left;">
            <table id="beautifulTable" class="display">
                <thead>
                    <!-- Headers will be loaded dynamically from CSV -->
                </thead>
                <tbody>
                    <!-- Data will be loaded dynamically from CSV -->
                </tbody>
            </table>

        </div>

        <!-- Custom CSS for centering -->
        <style>
            .center { text-align: center; }
            th.dt-center, td.dt-center { text-align: center; }
        </style>

        <!-- Initialize DataTable -->
        <script>
            $(document).ready(function() {
                // Load data from CSV
                $.ajax({
                    type: "GET",
                    url: "{{ data_source_url }}",
                    dataType: "text",
                    success: function(data) {
                        processData(data);
                    }
                });

                function processData(allText) {
                    var allTextLines = allText.split(/\r\n|\n/);
                    var headers = allTextLines[0].split(',');
                    var lines = [];

                    // Generate table headers
                    var headerHTML = '<tr>';
                    for (var i = 0; i < headers.length; i++) {
                        headerHTML += '<th class="center">' + headers[i] + '</th>';
                    }
                    headerHTML += '</tr>';
                    $('#beautifulTable thead').html(headerHTML);

                    for (var i = 1; i < allTextLines.length; i++) {
                        var data = allTextLines[i].split(',');
                        if (data.length == headers.length) {
                            var tarr = [];
                            for (var j = 0; j < headers.length; j++) {
                                tarr.push(data[j]);
                            }
                            lines.push(tarr);
                        }
                    }

                    // Populate DataTable
                    $('#beautifulTable').DataTable({
                        data: lines,
                        columns: headers.map(function(header) {
                            return { title: header, className: "center" };
                        }),
                        "lengthMenu": [10, 20, 50, 100],
                        "autoWidth": true // Enable auto column width adjustment
                    });
                }
            });
        </script>
    </body>
</html>

app.py

import streamlit as st
from jinja2 import Environment, FileSystemLoader


st.set_page_config(layout='wide')


def render_html_template(url, table_template, width=1200, height=600, scrolling=True):
    # Load Jinja environment
    env = Environment(loader=FileSystemLoader('.'))
    template = env.get_template(table_template)

    # Render the template with the provided URL
    rendered_html = template.render(data_source_url=url)

    # Render the HTML content
    st.components.v1.html(rendered_html, width=width, height=height, scrolling=scrolling)


def main():
    template = 'table_template.html'

    st.title('Miles per Gallon Dataset')
    url = 'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/mpg.csv'
    render_html_template(url, template, width=1300, height=600, scrolling=True)


if __name__ == '__main__':
    main()

Input your url here. Be careful on table size.

url = 'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/mpg.csv'

You can customize the table by modifying the html template.

Output

Very nice table, can I use a similar method to display pandas’ dataframe?

Great to see the above example @ferdy!

I have just added a Streamlit component that can render Pandas DataFrames as interactive DataTables to my ITables project:

from itables.streamlit import interactive_table

# Display a DF
interactive_table(df)

# Set a caption
interactive_table(df, caption="A Pandas DataFrame rendered as an interactive DataTable")

# AddCopy/CSV/Excel download buttons
interactive_table(df, buttons=["copyHtml5", "csvHtml5", "excelHtml5"])

Please note that ITables will downsample large tables by default (use the maxBytes argument to avoid this, or change the default, see Downsampling — Interactive Tables).

One of the current limitations is that, unlike in the notebook, we can’t seem to pass custom javascript callbacks to the Streamlit component. Is that something that would be an option in the above templated approach?

1 Like

Oh I see that actually ITables’ to_html_datatable function also works in the html component (but then I need to set a height, and also the table takes longer to display as the library is loaded from the web, so interactive_table is probably the way to go even if I don’t have the JS callbacks, isn’t it?)

from itables.streamlit import interactive_table
from itables import to_html_datatable
from itables.sample_dfs import get_countries
from streamlit.components.v1 import html
import streamlit as st

st.set_page_config(layout="wide")

df = get_countries(html=False)

html(to_html_datatable(df, caption="Table rendered with to_html_datatable"), height=560)

interactive_table(df, caption="Table rendered with the streamlit component")

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.