Interactive Tables

Great Tables can render a fully interactive table (with sorting, searching, filtering, and pagination) by calling opt_interactive(). The result is a self-contained HTML widget powered by DataTables that works in Jupyter notebooks, Quarto documents, and any HTML output.

Enabling interactivity

A single call to opt_interactive() switches the table from static to interactive. All of the normal formatting and styling methods continue to work exactly as before.

from great_tables import GT
from great_tables.data import gtcars
import polars as pl

gtcars_pl = pl.from_pandas(gtcars).select(["mfr", "model", "year", "hp", "trq", "msrp"])

(
    GT(gtcars_pl)
    .tab_header(title="GT Cars", subtitle="The full dataset, interactively")
    .cols_label(mfr="Make", model="Model", year="Year", hp="HP", trq="Torque", msrp="MSRP")
    .fmt_integer(columns=["year", "hp", "trq"], use_seps=False)
    .fmt_currency(columns="msrp")
    .opt_interactive()
)

GT Cars

The full dataset, interactively

Click on any column header to sort. The table is paginated by default with 10 rows per page.

Search and per-column filters

Pass use_search=True to add a global search box, and use_filters=True to add a per-column text filter row beneath the headers.

from great_tables import GT
from great_tables.data import gtcars
import polars as pl

(
    GT(gtcars_pl)
    .tab_header(title="GT Cars: Search and Filter")
    .cols_label(mfr="Make", model="Model", year="Year", hp="HP", trq="Torque", msrp="MSRP")
    .fmt_integer(columns=["year", "hp", "trq"], use_seps=False)
    .fmt_currency(columns="msrp")
    .opt_interactive(use_search=True, use_filters=True)
)

GT Cars: Search and Filter

Try typing "Audi" in the Make filter or "2017" in the Year filter to narrow the results.

Pagination options

Control the number of rows per page and let users choose from a list of sizes with page_size_default= and page_size_values=. The pagination style can also be changed with pagination_type= ("numbers", "simple", or "full").

from great_tables import GT
from great_tables.data import gtcars
import polars as pl

(
    GT(gtcars_pl)
    .tab_header(title="GT Cars: Pagination options")
    .cols_label(mfr="Make", model="Model", year="Year", hp="HP", trq="Torque", msrp="MSRP")
    .fmt_integer(columns=["year", "hp", "trq"], use_seps=False)
    .fmt_currency(columns="msrp")
    .opt_interactive(
        use_page_size_select=True,
        page_size_default=5,
        page_size_values=[5, 10, 25, 50],
        pagination_type="simple",
    )
)

GT Cars: Pagination options

Set use_pagination=False to display the full table without paging. This is useful for smaller datasets.

Styling with tab_style()

The tab_style() methods works in interactive mode for loc.body() and loc.column_labels(). Styles are applied row-by-row as DataTables renders each page, so they follow the data when you sort or filter.

from great_tables import style, loc

(
    GT(gtcars_pl)
    .tab_header(title="GT Cars: Targeted styles")
    .cols_label(mfr="Make", model="Model", year="Year", hp="HP", trq="Torque", msrp="MSRP")
    .fmt_integer(columns=["year", "hp", "trq"], use_seps=False)
    .fmt_currency(columns="msrp")
    .tab_style(
        style=style.fill(color="#fff3cd"),
        locations=loc.body(columns="hp", rows=pl.col("hp") > 500),
    )
    .tab_style(
        style=[style.text(color="#c0392b", weight="bold")],
        locations=loc.body(columns="msrp", rows=pl.col("msrp") > 200_000),
    )
    .tab_style(
        style=[style.fill(color="#ddeeff"), style.text(weight="bold")],
        locations=loc.column_labels(columns=["hp", "trq"]),
    )
    .opt_interactive(use_search=True, use_filters=True, page_size_default=10)
)

GT Cars: Targeted styles

From the output you might notice:

  • rows where HP exceeds 500 are highlighted in amber
  • MSRP values over $200,000 appear in bold red
  • the HP and Torque column headers have a blue-tinted background

Sort by HP or MSRP to confirm that the styling moves with the data.

Colorizing with data_color()

The use of data_color() is fully supported in interactive tables. It applies a continuous color scale to each cell based on the underlying value. Because data_color() is implemented via tab_style() internally, it inherits the same interactive behavior automatically.

(
    GT(gtcars_pl)
    .tab_header(title="GT Cars: data_color()")
    .cols_label(mfr="Make", model="Model", year="Year", hp="HP", trq="Torque", msrp="MSRP")
    .fmt_integer(columns=["year", "hp", "trq"], use_seps=False)
    .fmt_currency(columns="msrp")
    .data_color(columns="hp",  palette=["#fff7ec", "#ef6c00"])
    .data_color(columns="msrp", palette="Blues")
    .opt_interactive(use_search=True, use_filters=True, page_size_default=10)
)

GT Cars: data_color()

Each column uses its own independent scale: orange for HP and blue for MSRP. Try filtering by Make to see how the scales update in context.

Multiple columns, shared scale

Pass domain= to lock the color range across columns so they share a common scale.

(
    GT(gtcars_pl)
    .tab_header(
        title="GT Cars: HP and Torque on a shared scale",
        subtitle="Higher values are darker and both columns use the same domain",
    )
    .cols_label(mfr="Make", model="Model", year="Year", hp="HP", trq="Torque", msrp="MSRP")
    .fmt_integer(columns=["year", "hp", "trq"], use_seps=False)
    .fmt_currency(columns="msrp")
    .data_color(
        columns=["hp", "trq"],
        palette=["#edf8fb", "#006d2c"],
        domain=[200, 700],
    )
    .opt_interactive(use_search=True, page_size_default=10)
)

GT Cars: HP and Torque on a shared scale

Higher values are darker and both columns use the same domain