# Table Explorer

The [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) function generates an interactive table widget from tabular data. Where [`tbl_preview()`](table-previews.md) produces a static, JavaScript-free snapshot of a dataset, [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) embeds all rows as inline JSON and progressively enhances a static fallback table with interactive controls: sorting, token-based filtering, pagination, column toggling, copy-to-clipboard, and CSV download.

Like [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview), [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) accepts any tabular data source--Polars/Pandas DataFrames, PyArrow Tables, CSV/TSV/JSONL/Parquet/Feather files, column-oriented dicts, or row-oriented lists of dicts. The output is a self-contained HTML block that works in Python code cells, Jupyter notebooks, and (via the `{{< tbl-explorer >}}` shortcode) directly in Quarto `.qmd` pages.

> **Note: Note**
>
> For datasets larger than 10,000 rows, [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) emits a warning because all data is embedded as JSON, which increases page weight. For very large datasets, consider using [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview) with `n_head`/`n_tail` splitting instead.


# Basic Usage

Pass any supported data source to [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) to get a fully interactive table:


``` python
from great_docs import tbl_explorer

tbl_explorer({
    "city": ["Tokyo", "Paris", "New York", "London", "Sydney"],
    "population": [13960000, 2161000, 8336000, 8982000, 5312000],
    "country": ["Japan", "France", "USA", "UK", "Australia"],
})
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="4" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #F0F0F0; color: #333333; border: 1px solid #F0F0F0; margin-right: 8px;">Table</span>Rows5Columns3
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="city" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

city

<em>str</em>

</div></th>
<th id="population" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

population

<em>i64</em>

</div></th>
<th id="country" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

country

<em>str</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 146px">Tokyo</td>
<td class="gt_row gt_right" style="max-width: 166px">13960000</td>
<td class="gt_row gt_left" style="max-width: 153px">Japan</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 146px">Paris</td>
<td class="gt_row gt_right" style="max-width: 166px">2161000</td>
<td class="gt_row gt_left" style="max-width: 153px">France</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 146px">New York</td>
<td class="gt_row gt_right" style="max-width: 166px">8336000</td>
<td class="gt_row gt_left" style="max-width: 153px">USA</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 146px">London</td>
<td class="gt_row gt_right" style="max-width: 166px">8982000</td>
<td class="gt_row gt_left" style="max-width: 153px">UK</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 146px">Sydney</td>
<td class="gt_row gt_right" style="max-width: 166px">5312000</td>
<td class="gt_row gt_left" style="max-width: 153px">Australia</td>
</tr>
</tbody>
</table>


The result includes a toolbar with a filter bar, column toggle, copy, download, and reset buttons. Column headers are sortable, and pagination appears at the bottom when the dataset exceeds the page size.

> **Tip: Hiding the Code Cell**
>
> Most of the time you'll want to show just the table itself, not the underlying generation code. Add `#| echo: false` at the top of the code cell to hide the source and display only the rendered explorer:
>
> ```` markdown
> ```{python}
> #| echo: false
> tbl_explorer("data/students.csv")
> ```
> ````


# Sorting

Click any column header to cycle through three states: **ascending → descending → unsorted**. Sort indicators appear as arrows next to column names.

Multi-column sorting is always active. Clicking additional columns adds them to the sort stack, with each column sorting within the groups established by prior sort columns. For example, clicking *department* then *salary* will group by department and sort salaries within each department. To start over, use the reset button to clear all sort criteria.


# Token-Based Filtering

The filter system uses structured, token-based filters. Each filter is a discrete token displayed as a blue pill in the filter bar, and all active tokens are ANDed together.


## Adding a Filter

1.  Click the **+** button in the filter bar
2.  Select a **column** from the dropdown--each column shows its dtype badge
3.  Choose an **operator**--the available operators depend on the column's data type:

| Column type | Operators |
|----|----|
| String | contains, doesn't contain, starts with, ends with, equals, is empty, is not empty |
| Numeric | =, ≠, \<, ≤, \>, ≥, between |
| Boolean | is true, is false |
| All types | is null, is not null |

4.  For operators that require a value, type it in the input field and press **Enter** or click **Apply**

The *between* operator for numeric columns presents two input fields for the lower and upper bounds of the range.


## Understanding Filter Tokens

Each token shows the column name, operator, and value. Text values appear in single quotes (e.g., `city contains 'york'`). Numeric values are shown unquoted (e.g., `salary > 80000`). Click the **×** on any token to remove that individual filter.


## Case Sensitivity

Text filters are **case-insensitive** by default. When entering a filter value for a text column, an **Aa** toggle button appears next to the input field. Click it to enable case-sensitive matching for that specific filter. Case-sensitive tokens display a small bordered **Aa** badge on the pill.


## Combining Filters

Multiple filters are ANDed together--a row must match *all* active filters to be displayed. You can add multiple filters on the same column (e.g., `salary > 70000` AND `salary < 100000`) or across different columns (e.g., `department contains 'Eng'` AND `rating > 4.0`).


## Search Highlighting

When a "contains" filter is active, matching text in cells is highlighted with a colored background. This makes it easy to spot why each row matched the filter. Disable this with `search_highlight=False`.


# Pagination

Tables are paginated at **20 rows per page** by default. The pagination bar shows:

- the current range (e.g., "Showing 1-20 of 150 rows")
- **First**, **Previous**, **Next**, and **Last** navigation buttons
- page number buttons with ellipsis for large page counts

The row count updates to reflect any active filters (e.g., "Showing 1-5 of 5 rows (filtered from 24)").

To disable pagination and show all rows at once, set `page_size=0`:


``` python
tbl_explorer(
    "../assets/data/students.csv",
    page_size=0,
)
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="6" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #FFF8E1; color: #7A6200; border: 1px solid #FFF8E1; margin-right: 8px;">CSV</span>Rows10Columns5
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="name" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

name

<em>str</em>

</div></th>
<th id="subject" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

subject

<em>str</em>

</div></th>
<th id="score" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

score

<em>f64</em>

</div></th>
<th id="grade" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

grade

<em>str</em>

</div></th>
<th id="passed" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

passed

<em>bool</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 97px">Alice</td>
<td class="gt_row gt_left" style="max-width: 102px">Math</td>
<td class="gt_row gt_right" style="max-width: 86px">95.5</td>
<td class="gt_row gt_left" style="max-width: 86px">A</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 97px">Bob</td>
<td class="gt_row gt_left" style="max-width: 102px">Science</td>
<td class="gt_row gt_right" style="max-width: 86px">82</td>
<td class="gt_row gt_left" style="max-width: 86px">B</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 97px">Charlie</td>
<td class="gt_row gt_left" style="max-width: 102px">English</td>
<td class="gt_row gt_right" style="max-width: 86px">71.3</td>
<td class="gt_row gt_left" style="max-width: 86px">C</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 97px">Diana</td>
<td class="gt_row gt_left" style="max-width: 102px">History</td>
<td class="gt_row gt_right" style="max-width: 86px">60</td>
<td class="gt_row gt_left" style="max-width: 86px">D</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 97px">Eve</td>
<td class="gt_row gt_left" style="max-width: 102px">Art</td>
<td class="gt_row gt_right" style="max-width: 86px">55.8</td>
<td class="gt_row gt_left" style="max-width: 86px">F</td>
<td class="gt_row gt_left" style="max-width: 94px">False</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">5</td>
<td class="gt_row gt_left" style="max-width: 97px">Frank</td>
<td class="gt_row gt_left" style="max-width: 102px">Math</td>
<td class="gt_row gt_right" style="max-width: 86px">88.2</td>
<td class="gt_row gt_left" style="max-width: 86px">B+</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">6</td>
<td class="gt_row gt_left" style="max-width: 97px">Grace</td>
<td class="gt_row gt_left" style="max-width: 102px">Science</td>
<td class="gt_row gt_right" style="max-width: 86px">79.9</td>
<td class="gt_row gt_left" style="max-width: 86px">C+</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">7</td>
<td class="gt_row gt_left" style="max-width: 97px">Hank</td>
<td class="gt_row gt_left" style="max-width: 102px">English</td>
<td class="gt_row gt_right" style="max-width: 86px">91</td>
<td class="gt_row gt_left" style="max-width: 86px">A-</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">8</td>
<td class="gt_row gt_left" style="max-width: 97px">Iris</td>
<td class="gt_row gt_left" style="max-width: 102px">History</td>
<td class="gt_row gt_right" style="max-width: 86px">66.4</td>
<td class="gt_row gt_left" style="max-width: 86px">D+</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">9</td>
<td class="gt_row gt_left" style="max-width: 97px">Jack</td>
<td class="gt_row gt_left" style="max-width: 102px">Art</td>
<td class="gt_row gt_right" style="max-width: 86px">73.7</td>
<td class="gt_row gt_left" style="max-width: 86px">C</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
</tbody>
</table>


Or set a custom page size:


``` python
tbl_explorer(
    "../assets/data/employees.csv",
    page_size=10,
)
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="6" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #FFF8E1; color: #7A6200; border: 1px solid #FFF8E1; margin-right: 8px;">CSV</span>Rows24Columns5
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="name" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

name

<em>str</em>

</div></th>
<th id="department" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

department

<em>str</em>

</div></th>
<th id="salary" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

salary

<em>i64</em>

</div></th>
<th id="years" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

years

<em>i64</em>

</div></th>
<th id="rating" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

rating

<em>f64</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 90px">Alice</td>
<td class="gt_row gt_left" style="max-width: 119px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 87px">95000</td>
<td class="gt_row gt_right" style="max-width: 79px">5</td>
<td class="gt_row gt_right" style="max-width: 87px">4.5</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 90px">Bob</td>
<td class="gt_row gt_left" style="max-width: 119px">Marketing</td>
<td class="gt_row gt_right" style="max-width: 87px">72000</td>
<td class="gt_row gt_right" style="max-width: 79px">3</td>
<td class="gt_row gt_right" style="max-width: 87px">3.8</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 90px">Charlie</td>
<td class="gt_row gt_left" style="max-width: 119px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 87px">105000</td>
<td class="gt_row gt_right" style="max-width: 79px">8</td>
<td class="gt_row gt_right" style="max-width: 87px">4.9</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 90px">Diana</td>
<td class="gt_row gt_left" style="max-width: 119px">Sales</td>
<td class="gt_row gt_right" style="max-width: 87px">68000</td>
<td class="gt_row gt_right" style="max-width: 79px">2</td>
<td class="gt_row gt_right" style="max-width: 87px">3.2</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 90px">Eve</td>
<td class="gt_row gt_left" style="max-width: 119px">Marketing</td>
<td class="gt_row gt_right" style="max-width: 87px">88000</td>
<td class="gt_row gt_right" style="max-width: 79px">6</td>
<td class="gt_row gt_right" style="max-width: 87px">4.1</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">5</td>
<td class="gt_row gt_left" style="max-width: 90px">Frank</td>
<td class="gt_row gt_left" style="max-width: 119px">Sales</td>
<td class="gt_row gt_right" style="max-width: 87px">71000</td>
<td class="gt_row gt_right" style="max-width: 79px">4</td>
<td class="gt_row gt_right" style="max-width: 87px">3.7</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">6</td>
<td class="gt_row gt_left" style="max-width: 90px">Grace</td>
<td class="gt_row gt_left" style="max-width: 119px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 87px">92000</td>
<td class="gt_row gt_right" style="max-width: 79px">7</td>
<td class="gt_row gt_right" style="max-width: 87px">4.6</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">7</td>
<td class="gt_row gt_left" style="max-width: 90px">Hank</td>
<td class="gt_row gt_left" style="max-width: 119px">Marketing</td>
<td class="gt_row gt_right" style="max-width: 87px">76000</td>
<td class="gt_row gt_right" style="max-width: 79px">3</td>
<td class="gt_row gt_right" style="max-width: 87px">3.5</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">8</td>
<td class="gt_row gt_left" style="max-width: 90px">Iris</td>
<td class="gt_row gt_left" style="max-width: 119px">Sales</td>
<td class="gt_row gt_right" style="max-width: 87px">64000</td>
<td class="gt_row gt_right" style="max-width: 79px">1</td>
<td class="gt_row gt_right" style="max-width: 87px">2.9</td>
</tr>
<tr class="gd-tbl-divider">
<td class="gt_row gt_right gd-tbl-rownum">9</td>
<td class="gt_row gt_left" style="max-width: 90px">Jack</td>
<td class="gt_row gt_left" style="max-width: 119px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 87px">110000</td>
<td class="gt_row gt_right" style="max-width: 79px">9</td>
<td class="gt_row gt_right" style="max-width: 87px">4.8</td>
</tr>
</tbody>
</table>


# Column Toggle

Click the **Columns** button in the toolbar to open a dropdown with checkboxes for each column. Uncheck columns to hide them from the table; check them again to restore them. At least one column must remain visible--the last visible column's checkbox is disabled.

This is useful for wide datasets where you want to focus on specific columns without modifying the underlying data.


# Copy and Download

The toolbar includes two data-export buttons:

- **Copy** (clipboard icon) -- copies the currently visible page of data as tab-separated values to the clipboard. On success, the icon briefly turns into a green checkmark. The copied data includes column headers and respects the current sort and filter state.
- **Download** (download icon) -- downloads the full filtered dataset (all pages, not just the current page) as a CSV file. The filename is based on the table's `id` attribute.


# Reset

The **Reset** button (↺ icon) restores the table to its initial state in one click, clearing all:

- Sort states
- Filter tokens
- Column visibility changes
- Pagination (returns to page 1)


# From a File

Like [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview), you can pass a file path directly. The file extension determines the loader:


``` python
tbl_explorer("../assets/data/students.csv", page_size=5)
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="6" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #FFF8E1; color: #7A6200; border: 1px solid #FFF8E1; margin-right: 8px;">CSV</span>Rows10Columns5
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="name" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

name

<em>str</em>

</div></th>
<th id="subject" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

subject

<em>str</em>

</div></th>
<th id="score" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

score

<em>f64</em>

</div></th>
<th id="grade" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

grade

<em>str</em>

</div></th>
<th id="passed" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

passed

<em>bool</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 97px">Alice</td>
<td class="gt_row gt_left" style="max-width: 102px">Math</td>
<td class="gt_row gt_right" style="max-width: 86px">95.5</td>
<td class="gt_row gt_left" style="max-width: 86px">A</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 97px">Bob</td>
<td class="gt_row gt_left" style="max-width: 102px">Science</td>
<td class="gt_row gt_right" style="max-width: 86px">82</td>
<td class="gt_row gt_left" style="max-width: 86px">B</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 97px">Charlie</td>
<td class="gt_row gt_left" style="max-width: 102px">English</td>
<td class="gt_row gt_right" style="max-width: 86px">71.3</td>
<td class="gt_row gt_left" style="max-width: 86px">C</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 97px">Diana</td>
<td class="gt_row gt_left" style="max-width: 102px">History</td>
<td class="gt_row gt_right" style="max-width: 86px">60</td>
<td class="gt_row gt_left" style="max-width: 86px">D</td>
<td class="gt_row gt_left" style="max-width: 94px">True</td>
</tr>
<tr class="gd-tbl-divider">
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 97px">Eve</td>
<td class="gt_row gt_left" style="max-width: 102px">Art</td>
<td class="gt_row gt_right" style="max-width: 86px">55.8</td>
<td class="gt_row gt_left" style="max-width: 86px">F</td>
<td class="gt_row gt_left" style="max-width: 94px">False</td>
</tr>
</tbody>
</table>


Supported file formats: `.csv`, `.tsv`, `.jsonl` (or `.ndjson`), `.parquet`, `.feather`, `.arrow`.

> **Tip: Where to Put Data Files**
>
> We recommend placing data files in an `assets/data/` directory at the root of your project. This keeps them version-controlled, easy to reference from any `.qmd` page, and separate from images and other static assets:
>
>     my-package/
>     ├── assets/
>     │   └── data/
>     │       ├── students.csv
>     │       └── products.tsv
>     ├── reference/
>     └── user-guide/
>
> Both [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) and [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview) resolve file paths relative to the working directory, which is typically the project root during a Quarto build.


# From a DataFrame

Pass a Polars or Pandas DataFrame directly:


``` python
import polars as pl

df = pl.DataFrame({
    "product": ["Widget", "Gadget", "Gizmo", "Doohickey", "Thingamajig"],
    "category": ["Electronics", "Tools", "Kitchen", "Garden", "Office"],
    "price": [29.99, 49.50, 12.00, 8.75, 199.99],
    "in_stock": [True, False, True, True, False],
    "rating": [4.5, 3.8, 4.9, 4.2, 2.1],
})

tbl_explorer(df)
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="6" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #0075FF; color: #FFFFFF; border: 1px solid #0075FF; margin-right: 8px;">Polars</span>Rows5Columns5
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="product" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

product

<em>str</em>

</div></th>
<th id="category" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

category

<em>str</em>

</div></th>
<th id="price" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

price

<em>f64</em>

</div></th>
<th id="in_stock" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

in_stock

<em>bool</em>

</div></th>
<th id="rating" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

rating

<em>f64</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 110px">Widget</td>
<td class="gt_row gt_left" style="max-width: 110px">Electronics</td>
<td class="gt_row gt_right" style="max-width: 74px">29.99</td>
<td class="gt_row gt_left" style="max-width: 93px">True</td>
<td class="gt_row gt_right" style="max-width: 78px">4.5</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 110px">Gadget</td>
<td class="gt_row gt_left" style="max-width: 110px">Tools</td>
<td class="gt_row gt_right" style="max-width: 74px">49.5</td>
<td class="gt_row gt_left" style="max-width: 93px">False</td>
<td class="gt_row gt_right" style="max-width: 78px">3.8</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 110px">Gizmo</td>
<td class="gt_row gt_left" style="max-width: 110px">Kitchen</td>
<td class="gt_row gt_right" style="max-width: 74px">12</td>
<td class="gt_row gt_left" style="max-width: 93px">True</td>
<td class="gt_row gt_right" style="max-width: 78px">4.9</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 110px">Doohickey</td>
<td class="gt_row gt_left" style="max-width: 110px">Garden</td>
<td class="gt_row gt_right" style="max-width: 74px">8.75</td>
<td class="gt_row gt_left" style="max-width: 93px">True</td>
<td class="gt_row gt_right" style="max-width: 78px">4.2</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 110px">Thingamajig</td>
<td class="gt_row gt_left" style="max-width: 110px">Office</td>
<td class="gt_row gt_right" style="max-width: 74px">199.99</td>
<td class="gt_row gt_left" style="max-width: 93px">False</td>
<td class="gt_row gt_right" style="max-width: 78px">2.1</td>
</tr>
</tbody>
</table>


# Column Subset

Use the `columns=` parameter to show only specific columns:


``` python
tbl_explorer(
    "../assets/data/employees.csv",
    columns=["name", "department", "salary"],
)
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="4" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #FFF8E1; color: #7A6200; border: 1px solid #FFF8E1; margin-right: 8px;">CSV</span>Rows24Columns5
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="name" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

name

<em>str</em>

</div></th>
<th id="department" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

department

<em>str</em>

</div></th>
<th id="salary" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

salary

<em>i64</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 146px">Alice</td>
<td class="gt_row gt_left" style="max-width: 175px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 143px">95000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 146px">Bob</td>
<td class="gt_row gt_left" style="max-width: 175px">Marketing</td>
<td class="gt_row gt_right" style="max-width: 143px">72000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 146px">Charlie</td>
<td class="gt_row gt_left" style="max-width: 175px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 143px">105000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 146px">Diana</td>
<td class="gt_row gt_left" style="max-width: 175px">Sales</td>
<td class="gt_row gt_right" style="max-width: 143px">68000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 146px">Eve</td>
<td class="gt_row gt_left" style="max-width: 175px">Marketing</td>
<td class="gt_row gt_right" style="max-width: 143px">88000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">5</td>
<td class="gt_row gt_left" style="max-width: 146px">Frank</td>
<td class="gt_row gt_left" style="max-width: 175px">Sales</td>
<td class="gt_row gt_right" style="max-width: 143px">71000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">6</td>
<td class="gt_row gt_left" style="max-width: 146px">Grace</td>
<td class="gt_row gt_left" style="max-width: 175px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 143px">92000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">7</td>
<td class="gt_row gt_left" style="max-width: 146px">Hank</td>
<td class="gt_row gt_left" style="max-width: 175px">Marketing</td>
<td class="gt_row gt_right" style="max-width: 143px">76000</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">8</td>
<td class="gt_row gt_left" style="max-width: 146px">Iris</td>
<td class="gt_row gt_left" style="max-width: 175px">Sales</td>
<td class="gt_row gt_right" style="max-width: 143px">64000</td>
</tr>
<tr class="gd-tbl-divider">
<td class="gt_row gt_right gd-tbl-rownum">9</td>
<td class="gt_row gt_left" style="max-width: 146px">Jack</td>
<td class="gt_row gt_left" style="max-width: 175px">Engineering</td>
<td class="gt_row gt_right" style="max-width: 143px">110000</td>
</tr>
</tbody>
</table>


# Quarto Shortcode

The `{{< tbl-explorer >}}` shortcode brings the interactive explorer to `.qmd` pages without writing any Python. The `file=` parameter specifies the data file path relative to the `.qmd` file:

``` {markdown}
```

All parameters can be set as shortcode attributes:

``` {markdown}
{{< tbl-explorer file="data.csv" page_size="25" sortable="true" >}}
{{< tbl-explorer file="data.csv" column_toggle="false" downloadable="false" >}}
{{< tbl-explorer file="data.csv" columns="name,department,salary" >}}
```

Boolean parameters accept `"true"` or `"false"` as strings. The `columns=` parameter accepts a comma-separated list of column names.


# Minimal Chrome

You can selectively disable interactive features for a cleaner, read-only presentation. For example, to show a sortable-only table with no toolbar controls:


``` python
tbl_explorer(
    "../assets/data/products.tsv",
    sortable=True,
    filterable=False,
    column_toggle=False,
    copyable=False,
    downloadable=False,
    page_size=0,
)
```


<table class="gt_table" data-quarto-disable-processing="true" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<th colspan="6" class="gt_heading gt_title gt_font_normal"><div style="padding-top: 0; padding-bottom: 7px;">
<span class="gd-tbl-badge" style="background-color: #E8F5E9; color: #2E7D32; border: 1px solid #E8F5E9; margin-right: 8px;">TSV</span>Rows7Columns5
</div></th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"></th>
<th id="product" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

product

<em>str</em>

</div></th>
<th id="category" class="gt_col_heading gt_columns_bottom_border gt_left" scope="col"><div>

category

<em>str</em>

</div></th>
<th id="price" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

price

<em>f64</em>

</div></th>
<th id="stock" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

stock

<em>i64</em>

</div></th>
<th id="rating" class="gt_col_heading gt_columns_bottom_border gt_right" scope="col"><div>

rating

<em>f64</em>

</div></th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr>
<td class="gt_row gt_right gd-tbl-rownum">0</td>
<td class="gt_row gt_left" style="max-width: 114px">Widget</td>
<td class="gt_row gt_left" style="max-width: 114px">Electronics</td>
<td class="gt_row gt_right" style="max-width: 78px">29.99</td>
<td class="gt_row gt_right" style="max-width: 74px">150</td>
<td class="gt_row gt_right" style="max-width: 82px">4.5</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">1</td>
<td class="gt_row gt_left" style="max-width: 114px">Gadget</td>
<td class="gt_row gt_left" style="max-width: 114px">Tools</td>
<td class="gt_row gt_right" style="max-width: 78px">49.5</td>
<td class="gt_row gt_right" style="max-width: 74px">80</td>
<td class="gt_row gt_right" style="max-width: 82px">3.8</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">2</td>
<td class="gt_row gt_left" style="max-width: 114px">Gizmo</td>
<td class="gt_row gt_left" style="max-width: 114px">Kitchen</td>
<td class="gt_row gt_right" style="max-width: 78px">12</td>
<td class="gt_row gt_right" style="max-width: 74px">300</td>
<td class="gt_row gt_right" style="max-width: 82px">4.9</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">3</td>
<td class="gt_row gt_left" style="max-width: 114px">Doohickey</td>
<td class="gt_row gt_left" style="max-width: 114px">Garden</td>
<td class="gt_row gt_right" style="max-width: 78px">8.75</td>
<td class="gt_row gt_right" style="max-width: 74px">0</td>
<td class="gt_row gt_right" style="max-width: 82px">4.2</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">4</td>
<td class="gt_row gt_left" style="max-width: 114px">Thingamajig</td>
<td class="gt_row gt_left" style="max-width: 114px">Office</td>
<td class="gt_row gt_right" style="max-width: 78px">199.99</td>
<td class="gt_row gt_right" style="max-width: 74px">25</td>
<td class="gt_row gt_right" style="max-width: 82px">2.1</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">5</td>
<td class="gt_row gt_left" style="max-width: 114px">Contraption</td>
<td class="gt_row gt_left" style="max-width: 114px">Electronics</td>
<td class="gt_row gt_right" style="max-width: 78px">65</td>
<td class="gt_row gt_right" style="max-width: 74px">44</td>
<td class="gt_row gt_right" style="max-width: 82px">3.5</td>
</tr>
<tr>
<td class="gt_row gt_right gd-tbl-rownum">6</td>
<td class="gt_row gt_left" style="max-width: 114px">Apparatus</td>
<td class="gt_row gt_left" style="max-width: 114px">Tools</td>
<td class="gt_row gt_right" style="max-width: 78px">120</td>
<td class="gt_row gt_right" style="max-width: 74px">12</td>
<td class="gt_row gt_right" style="max-width: 82px">4.7</td>
</tr>
</tbody>
</table>


# Progressive Enhancement

Interactive widgets can be fragile: if JavaScript fails to load or the page is consumed outside a browser (RSS readers, email digests, search engine crawlers), readers see nothing. The table explorer avoids this problem.

The [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) output is designed with **progressive enhancement** in mind. The initial HTML contains a fully rendered static table (the first page of data) that is readable without JavaScript. When JavaScript is available, the static table is enhanced with interactive controls.

This means:

- **RSS feeds and email**: readers see a usable static table
- **Search engines**: table content is indexable
- **Accessibility**: the base table works with screen readers
- **No-JS environments**: content is still visible

Because the static fallback is a real HTML table (not a placeholder), it carries semantic structure that assistive technology and search engines can parse directly.


# Choosing Between [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview) and [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer)

Great Docs offers two table display functions that serve different purposes. Use this comparison to decide which one fits your page:

|  | [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview) | [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) |
|----|----|----|
| **Interactivity** | None (static HTML) | Sorting, filtering, pagination, column toggle |
| **JavaScript** | Not required | Required for interactivity; static fallback without it |
| **Data embedding** | Head/tail rows only | All rows as inline JSON |
| **Best for** | Quick dataset snapshots, lightweight pages | Exploratory data docs, dashboards, reference tables |
| **Large datasets** | Efficient (shows only head + tail) | All data embedded (watch page weight) |
| **Dark mode** | Full support | Full support |

In general, reach for [tbl_preview()](../reference/tbl_preview.md#great_docs.tbl_preview) when you want a quick, lightweight snapshot and [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) when readers need to search, sort, or page through the data.


# Parameters Reference

All parameters for [tbl_explorer()](../reference/tbl_explorer.md#great_docs.tbl_explorer) are listed below. Every parameter has a sensible default, so in most cases you only need to pass the data source itself.

| Parameter | Type | Default | Description |
|----|----|----|----|
| `data` | various | -- | Data source: DataFrame, file path, dict, or list of dicts |
| `columns` | `list[str]` | `None` | Column subset to display (all if `None`) |
| `show_row_numbers` | `bool` | `True` | Show row-number gutter column |
| `show_dtypes` | `bool` | `True` | Show dtype labels under column names |
| `show_dimensions` | `bool` | `True` | Show header banner with type badge and counts |
| `max_col_width` | `int` | `250` | Maximum column width in pixels |
| `min_tbl_width` | `int` | `500` | Minimum total table width in pixels |
| `caption` | `str` | `None` | Caption text above column headers |
| `highlight_missing` | `bool` | `True` | Highlight `None`/`NaN`/`NA` in red |
| `page_size` | `int` | `20` | Rows per page (`0` to disable pagination) |
| `sortable` | `bool` | `True` | Enable click-to-sort on column headers |
| `filterable` | `bool` | `True` | Enable the token-based filter bar |
| `column_toggle` | `bool` | `True` | Enable column visibility dropdown |
| `copyable` | `bool` | `True` | Enable copy-to-clipboard button |
| `downloadable` | `bool` | `True` | Enable CSV download button |
| `resizable` | `bool` | `False` | Enable column drag-resize (reserved for future use) |
| `sticky_header` | `bool` | `True` | Make column headers sticky on vertical scroll |
| `search_highlight` | `bool` | `True` | Highlight matching text for "contains" filters |
| `id` | `str` | auto | Custom HTML `id` for the container |

The return value is a `TblExplorer` object with `_repr_html_()` (for notebook display), `as_html()` (returns the HTML string), and `save(path)` (writes HTML to a file) methods.


# Next Steps

The table explorer turns static data tables into interactive views with filtering, sorting, pagination, and CSV export. Use it when readers need to search through larger datasets or compare specific rows.

- [Table Previews](table-previews.md) covers a lighter-weight table display for quick data summaries
- [Scale-to-Fit](scale-to-fit.md) keeps wide tables readable on narrow screens
- [Configuration](configuration.md) covers all `great-docs.yml` options
