Documentation sites for data-oriented packages often need to show what a dataset looks like. Screenshots go stale, raw HTML tables lack context, and notebook-style output is tightly coupled to a specific runtime. The tbl_preview() function solves this by generating a self-contained HTML table preview from almost any data source. It works in Python code cells, Jupyter notebooks, and (via the {{< tbl-preview >}} shortcode) directly in Quarto .qmd pages without writing any Python at all.
Each preview includes a colored type badge (Polars, Pandas, CSV, Parquet, etc.), compact dtype labels beneath every column header, row-number gutters, head/tail splitting for large tables, and automatic highlighting of missing values. The output renders identically in light mode and dark mode, requires no JavaScript, and is safe to embed in RSS feeds, emails, or static HTML.
This guide walks through every feature, starting from the simplest Python call and building up to the Quarto shortcode, file format support, and advanced display options. For interactive tables with sorting, filtering, and pagination, see the companion Table Explorer guide.
Quick Start
The fastest way to preview a table is to pass a Python dictionary to tbl_preview(). No configuration or file is needed.
from great_docs import tbl_preview
tbl_preview({
"city": ["Tokyo", "Paris", "New York"],
"country": ["Japan", "France", "USA"],
"population": [13960000, 2161000, 8336000],
})
TableRows3Columns3 |
|
|
|
|
| 0 |
Tokyo |
Japan |
13960000 |
| 1 |
Paris |
France |
2161000 |
| 2 |
New York |
USA |
8336000 |
The result is a styled HTML table with a header banner showing the row and column count, dtype labels beneath each column name, and row numbers on the left. This single function call is all it takes to go from raw data to a polished preview.
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 preview:
```{python}
#| echo: false
tbl_preview({"city": ["Tokyo", "Paris"], "country": ["Japan", "France"]})
```
Data Sources
tbl_preview() accepts many different input types, so you can pass whatever data structure your project already uses. Each source type gets a distinct colored badge in the header banner, making it easy to tell at a glance what kind of data is being displayed.
Dictionaries and Lists
The simplest inputs are column-oriented dictionaries and row-oriented lists of dictionaries. Both produce a Table badge and infer dtypes from the cell values.
A column-oriented dictionary (keys are column names, values are lists):
tbl_preview({"name": ["Alice", "Bob"], "score": [95.5, 82.0]})
TableRows2Columns2 |
|
|
|
| 0 |
Alice |
95.5 |
| 1 |
Bob |
82 |
A list of row dictionaries (each dict is one row):
tbl_preview([
{"name": "Alice", "score": 95.5},
{"name": "Bob", "score": 82.0},
])
TableRows2Columns2 |
|
|
|
| 0 |
Alice |
95.5 |
| 1 |
Bob |
82 |
Both styles produce identical output. Use whichever matches the data you already have.
Polars DataFrames
If Polars is installed, pass a pl.DataFrame directly. The badge shows Polars in blue, and dtype labels are derived from the Polars schema (e.g., i64, f64, str).
import polars as pl
df = pl.DataFrame({
"product": ["Widget", "Gadget", "Gizmo"],
"price": [29.99, 49.50, 12.00],
"in_stock": [True, False, True],
})
tbl_preview(df)
PolarsRows3Columns3 |
|
|
|
|
| 0 |
Widget |
29.99 |
True |
| 1 |
Gadget |
49.5 |
False |
| 2 |
Gizmo |
12 |
True |
Polars dtypes are mapped to compact short labels (like i64 for Int64, f64 for Float64, str for String) so the column headers stay clean.
Pandas DataFrames
Pandas DataFrames work the same way. The badge shows Pandas in dark purple, and dtypes come from the Pandas .dtypes attribute.
import pandas as pd
df = pd.DataFrame({
"city": ["London", "Sydney", "Toronto"],
"temp_c": [15.2, 22.8, -3.1],
"sunny": [False, True, False],
})
tbl_preview(df)
PandasRows3Columns3 |
|
|
|
|
| 0 |
London |
15.2 |
False |
| 1 |
Sydney |
22.8 |
True |
| 2 |
Toronto |
-3.1 |
False |
Both Polars and Pandas DataFrames are detected automatically (no format flag is needed).
CSV Files
Pass a file path (as a string or pathlib.Path) to any supported format and tbl_preview() reads it directly. CSV files get a warm yellow CSV badge:
tbl_preview("../assets/data/students.csv")
CSVRows10Columns5 |
|
|
|
|
|
|
| 0 |
Alice |
Math |
95.5 |
A |
True |
| 1 |
Bob |
Science |
82 |
B |
True |
| 2 |
Charlie |
English |
71.3 |
C |
True |
| 3 |
Diana |
History |
60 |
D |
True |
| 4 |
Eve |
Art |
55.8 |
F |
False |
| 5 |
Frank |
Math |
88.2 |
B+ |
True |
| 6 |
Grace |
Science |
79.9 |
C+ |
True |
| 7 |
Hank |
English |
91 |
A- |
True |
| 8 |
Iris |
History |
66.4 |
D+ |
True |
| 9 |
Jack |
Art |
73.7 |
C |
True |
The file is read using Polars if available, falling back to Pandas. Column dtypes are inferred from the file contents.
TSV Files
Tab-separated files (.tsv or .tab) are auto-detected by extension. The badge shows TSV in green:
tbl_preview("../assets/data/products.tsv")
TSVRows7Columns5 |
|
|
|
|
|
|
| 0 |
Widget |
Electronics |
29.99 |
150 |
4.5 |
| 1 |
Gadget |
Tools |
49.5 |
80 |
3.8 |
| 2 |
Gizmo |
Kitchen |
12 |
300 |
4.9 |
| 3 |
Doohickey |
Garden |
8.75 |
0 |
4.2 |
| 4 |
Thingamajig |
Office |
199.99 |
25 |
2.1 |
| 5 |
Contraption |
Electronics |
65 |
44 |
3.5 |
| 6 |
Apparatus |
Tools |
120 |
12 |
4.7 |
TSV support uses the same reader as CSV with the delimiter set to tab, so all the same options (column subsets, head/tail, etc.) apply.
JSONL Files
Newline-delimited JSON files (.jsonl or .ndjson) are read line by line. Each line must be a valid JSON object. The badge shows JSONL in light blue:
tbl_preview("../assets/data/server_logs.jsonl", show_all=True)
JSONLRows6Columns4 |
|
|
|
|
|
| 0 |
2025-01-15T08:30:00 |
INFO |
auth |
User login successful |
| 1 |
2025-01-15T08:31:12 |
WARNING |
db |
Slow query detected (3.2s) |
| 2 |
2025-01-15T08:32:45 |
ERROR |
api |
Request timeout on /v2/users |
| 3 |
2025-01-15T08:33:01 |
INFO |
cache |
Cache miss for key user:42 |
| 4 |
2025-01-15T08:34:20 |
DEBUG |
auth |
Token refresh for session abc123 |
| 5 |
2025-01-15T08:35:55 |
ERROR |
db |
Connection pool exhausted |
JSONL is a common format for log data and streaming pipelines, making tbl_preview() a convenient way to inspect log files in documentation.
Parquet Files
Apache Parquet files (.parquet or .pq) are read via Polars or PyArrow. The badge shows Parquet in purple:
tbl_preview("../assets/data/products.parquet", show_all=True)
ParquetRows5Columns5 |
|
|
|
|
|
|
| 0 |
Widget |
Electronics |
29.99 |
True |
4.5 |
| 1 |
Gadget |
Tools |
49.5 |
False |
3.8 |
| 2 |
Gizmo |
Kitchen |
12 |
True |
4.9 |
| 3 |
Doohickey |
Garden |
8.75 |
True |
4.2 |
| 4 |
Thingamajig |
Office |
199.99 |
False |
2.1 |
Parquet preserves the original column types from the file schema, so dtype labels are precise (e.g., f64 rather than a generic float).
Feather and Arrow IPC Files
Feather files (.feather) and Arrow IPC files (.arrow, .ipc) are both read as Arrow IPC format. Feather files get a Feather badge in orange, while .arrow/.ipc files get an Arrow badge in indigo:
tbl_preview("../assets/data/employees.feather", show_all=True)
FeatherRows6Columns4 |
|
|
|
|
|
| 0 |
Alice |
Engineering |
95000 |
5 |
| 1 |
Bob |
Marketing |
72000 |
3 |
| 2 |
Charlie |
Engineering |
105000 |
8 |
| 3 |
Diana |
Sales |
68000 |
2 |
| 4 |
Eve |
Marketing |
88000 |
6 |
| 5 |
Frank |
Sales |
71000 |
4 |
Arrow IPC is the recommended format for fast local reads when you need to preserve exact column types.
PyArrow Tables
If you’re working with PyArrow directly, pass a pyarrow.Table without writing to disk. The badge shows Arrow in indigo:
import pyarrow as pa
table = pa.table({
"sensor": ["A1", "A2", "B1"],
"reading": [23.5, 19.8, 31.2],
"active": [True, True, False],
})
tbl_preview(table)
ArrowRows3Columns3 |
|
|
|
|
| 0 |
A1 |
23.5 |
True |
| 1 |
A2 |
19.8 |
True |
| 2 |
B1 |
31.2 |
False |
PyArrow’s type system is fully supported, including nested types like list<int64> and struct which are displayed with their Arrow type names.
Supported File Extensions
The full mapping of file extensions to format types is:
.csv |
CSV |
CSV |
.tsv, .tab |
TSV |
TSV |
.jsonl, .ndjson |
JSONL |
JSONL |
.parquet, .pq |
Parquet |
Parquet |
.feather |
Feather |
Feather |
.arrow, .ipc |
Arrow IPC |
Arrow |
Any unrecognized extension defaults to CSV parsing.
Head and Tail Splitting
Large tables are automatically split into a head and tail section, connected by a visual divider row. This gives readers a sense of both the beginning and end of the data without rendering thousands of rows.
Default Split
By default, the first 5 rows and last 5 rows are shown:
import polars as pl
df = pl.DataFrame({
"id": list(range(1, 21)),
"value": [x * 1.1 for x in range(1, 21)],
"label": [f"item-{i}" for i in range(1, 21)],
})
tbl_preview(df)
PolarsRows20Columns3 |
|
|
|
|
| 0 |
1 |
1.1 |
item-1 |
| 1 |
2 |
2.2 |
item-2 |
| 2 |
3 |
3.3 |
item-3 |
| 3 |
4 |
4.4 |
item-4 |
| 4 |
5 |
5.5 |
item-5 |
| 15 |
16 |
17.6 |
item-16 |
| 16 |
17 |
18.7 |
item-17 |
| 17 |
18 |
19.8 |
item-18 |
| 18 |
19 |
20.9 |
item-19 |
| 19 |
20 |
22 |
item-20 |
The divider row between head and tail is a thin colored line that visually separates the two sections while keeping the table compact.
Custom Split
Adjust the n_head and n_tail parameters to control how many rows appear in each section:
tbl_preview(df, n_head=3, n_tail=2)
PolarsRows20Columns3 |
|
|
|
|
| 0 |
1 |
1.1 |
item-1 |
| 1 |
2 |
2.2 |
item-2 |
| 2 |
3 |
3.3 |
item-3 |
| 18 |
19 |
20.9 |
item-19 |
| 19 |
20 |
22 |
item-20 |
Setting n_tail=0 shows only the head, which is useful when the start of the data is most relevant:
tbl_preview(df, n_head=8, n_tail=0)
PolarsRows20Columns3 |
|
|
|
|
| 0 |
1 |
1.1 |
item-1 |
| 1 |
2 |
2.2 |
item-2 |
| 2 |
3 |
3.3 |
item-3 |
| 3 |
4 |
4.4 |
item-4 |
| 4 |
5 |
5.5 |
item-5 |
| 5 |
6 |
6.6 |
item-6 |
| 6 |
7 |
7.7 |
item-7 |
| 7 |
8 |
8.8 |
item-8 |
The limit parameter (default 50) caps the maximum sum of n_head + n_tail. If you need a larger split, increase the limit explicitly.
Show All Rows
For small tables where every row matters, set show_all=True to disable the head/tail split entirely:
tbl_preview(
{"name": ["Alice", "Bob", "Charlie"], "score": [95, 82, 71]},
show_all=True,
)
TableRows3Columns2 |
|
|
|
| 0 |
Alice |
95 |
| 1 |
Bob |
82 |
| 2 |
Charlie |
71 |
When show_all=True is set, the divider row is omitted and all rows are displayed in sequence. Use this for tables with fewer than ~50 rows where the full picture is important.
Column Selection
By default, all columns are shown. The columns parameter lets you pick a subset, which is useful for wide tables where only a few columns are relevant to the discussion.
tbl_preview(
"../assets/data/students.csv",
columns=["name", "score", "grade"],
show_all=True,
)
CSVRows10Columns5 |
|
|
|
|
| 0 |
Alice |
95.5 |
A |
| 1 |
Bob |
82 |
B |
| 2 |
Charlie |
71.3 |
C |
| 3 |
Diana |
60 |
D |
| 4 |
Eve |
55.8 |
F |
| 5 |
Frank |
88.2 |
B+ |
| 6 |
Grace |
79.9 |
C+ |
| 7 |
Hank |
91 |
A- |
| 8 |
Iris |
66.4 |
D+ |
| 9 |
Jack |
73.7 |
C |
Columns appear in the order you specify, so you can also use this to reorder columns. If a column name doesn’t exist in the data, a ValueError is raised with a helpful message listing the available columns.
Missing Values
Missing values (None, NaN, NA, Inf, -Inf) are highlighted with a red background and text by default. This makes it easy to spot gaps in the data at a glance.
import math
tbl_preview({
"sensor": ["A1", "A2", "A3", "B1"],
"reading": [23.5, None, math.nan, 31.2],
"status": ["ok", "offline", None, "ok"],
}, show_all=True)
TableRows4Columns3 |
|
|
|
|
| 0 |
A1 |
23.5 |
ok |
| 1 |
A2 |
None |
offline |
| 2 |
A3 |
NaN |
None |
| 3 |
B1 |
31.2 |
ok |
The cell text shows a representation of the missing value (None, NaN, NA, Inf), and the cell background turns light red to draw attention.
To disable missing-value highlighting (for example, if your data intentionally contains None as a meaningful value), set highlight_missing=False:
tbl_preview({
"sensor": ["A1", "A2", "A3"],
"reading": [23.5, None, 31.2],
}, show_all=True, highlight_missing=False)
TableRows3Columns2 |
|
|
|
| 0 |
A1 |
23.5 |
| 1 |
A2 |
None |
| 2 |
A3 |
31.2 |
With highlighting off, missing values still display their text representation but use the normal cell styling.
Display Options
Every visual element of the preview table can be toggled on or off independently. This lets you create anything from a fully-featured data preview to a minimal, stripped-down table.
Captions
Add a descriptive caption that appears below the header banner and above the column labels:
tbl_preview(
{"city": ["Tokyo", "Paris", "New York"], "population": [13960000, 2161000, 8336000]},
show_all=True,
caption="World cities by population (2024 estimates)",
)
TableRows3Columns2 |
| World cities by population (2024 estimates) |
|
|
|
| 0 |
Tokyo |
13960000 |
| 1 |
Paris |
2161000 |
| 2 |
New York |
8336000 |
Captions are useful when multiple previews appear on the same page, giving each table a distinct label.
Row Numbers
Row numbers appear in a left-hand gutter by default, starting from 0 to match Python, Polars, and Pandas 0-based indexing. They reflect the original row positions, even when head/tail splitting is active. To hide them:
tbl_preview(
{"x": [1, 2, 3], "y": [4, 5, 6]},
show_all=True,
show_row_numbers=False,
)
TableRows3Columns2 |
|
|
| 1 |
4 |
| 2 |
5 |
| 3 |
6 |
Hiding row numbers is useful for small reference tables where the row position isn’t meaningful.
Row Index Offset
If you prefer 1-based numbering (or any other starting value), set row_index_offset:
tbl_preview(
{"x": [1, 2, 3], "y": [4, 5, 6]},
show_all=True,
row_index_offset=1,
)
TableRows3Columns2 |
|
|
|
| 1 |
1 |
4 |
| 2 |
2 |
5 |
| 3 |
3 |
6 |
The offset applies to all rows, including tail rows in head/tail splits. The default of 0 keeps row numbers consistent with what you see when inspecting a DataFrame in Python.
Dtype Labels
Compact dtype labels appear beneath each column name (e.g., str, i64, f64). To hide them:
tbl_preview(
{"name": ["Alice", "Bob"], "score": [95.5, 82.0]},
show_all=True,
show_dtypes=False,
)
TableRows2Columns2 |
|
name |
score |
| 0 |
Alice |
95.5 |
| 1 |
Bob |
82 |
Hiding dtype labels produces a cleaner look when the column types are obvious from context or not relevant to the reader.
Dimensions Banner
The header banner shows the table type badge, row count, and column count. To hide it:
tbl_preview(
{"x": [1, 2, 3], "y": [4, 5, 6]},
show_all=True,
show_dimensions=False,
)
Hiding the dimensions banner is useful when the surrounding text already describes the data size.
Minimal Chrome
Combine the toggle options to create a stripped-down table with no row numbers, no dtypes, and no dimensions (just the data):
tbl_preview(
{"name": ["Alice", "Bob", "Charlie"], "score": [95, 82, 71]},
show_all=True,
show_row_numbers=False,
show_dtypes=False,
show_dimensions=False,
)
| name |
score |
| Alice |
95 |
| Bob |
82 |
| Charlie |
71 |
This minimal style works well for small inline reference tables where the chrome would be distracting.
Column Width Control
The max_col_width parameter caps the pixel width of any single column. Long cell values are truncated with an ellipsis. The default is 250px.
tbl_preview({
"description": [
"This is a very long description that would normally extend the column width quite far",
"Another lengthy entry with lots of detail about the item in question",
"Short one",
],
"code": ["ABC-001", "DEF-002", "GHI-003"],
}, show_all=True, max_col_width=150)
TableRows3Columns2 |
|
|
|
| 0 |
This is a very long description that would normally extend the column width quite far |
ABC-001 |
| 1 |
Another lengthy entry with lots of detail about the item in question |
DEF-002 |
| 2 |
Short one |
GHI-003 |
The min_tbl_width parameter sets a floor for the total table width (default 500px), ensuring that narrow tables don’t collapse to an unreadably small size.
Both width parameters accept integer values in pixels.
Quarto Shortcode
The {{< tbl-preview >}} shortcode renders a table preview directly in a .qmd page without writing any Python code. Point it at a data file and the shortcode handles everything.
Basic Usage
The simplest invocation needs only the file parameter:
{{< tbl-preview file="assets/data.csv" >}}
The shortcode resolves the file path relative to the Quarto project root, reads the data, and inserts the preview HTML at render time. No code cell or Python import is needed.
Adding Options
All the display options available to the Python function are also available as shortcode parameters:
{{< tbl-preview file="assets/data.csv" show_all="true" caption="My Dataset" >}}
{{< tbl-preview file="data.tsv" n_head="3" n_tail="2" show_row_numbers="false" >}}
{{< tbl-preview file="logs.jsonl" show_all="true" max_col_width="120" >}}
Note that shortcode parameters are always strings, so boolean values must be quoted ("true" / "false") and numeric values must be quoted ("10", "150").
Column Subsets
To show only specific columns, pass a comma-separated list to the columns parameter:
{{< tbl-preview file="assets/students.csv" columns="name,score,grade" show_all="true" >}}
Columns appear in the order listed.
Supported Parameters
The full set of shortcode parameters mirrors the Python function:
file |
string |
— |
Path to data file (required, relative to project root) |
columns |
string |
all |
Comma-separated column names to display |
n_head |
string |
"5" |
Number of rows from the start |
n_tail |
string |
"5" |
Number of rows from the end |
show_all |
string |
"false" |
Show all rows (ignores head/tail) |
show_row_numbers |
string |
"true" |
Show row-number gutter |
show_dtypes |
string |
"true" |
Show dtype labels under column names |
show_dimensions |
string |
"true" |
Show header banner with row/column counts |
max_col_width |
string |
"250" |
Maximum column width in pixels |
min_tbl_width |
string |
"500" |
Minimum total table width in pixels |
caption |
string |
— |
Caption text below the header banner |
row_index_offset |
string |
"0" |
Starting row number (use "1" for 1-based) |
The shortcode supports every file format listed in the Supported File Extensions table.
Notebook Integration
In Jupyter notebooks and Quarto .qmd documents with Python code cells, you can enable automatic tbl_preview() rendering for all DataFrames using the display hook.
Enable Auto-Preview
Call enable_tbl_preview() once at the top of a notebook, and every Polars or Pandas DataFrame displayed afterward will render as a preview table instead of the library’s default HTML:
import great_docs as gd
gd.enable_tbl_preview(n_head=8, n_tail=3)
# Now any DataFrame as the last expression in a cell
# renders as a tbl_preview() table automatically:
import pandas as pd
pd.read_csv("data.csv") # → preview table
Keyword arguments passed to enable_tbl_preview() are forwarded to tbl_preview(), so you can set defaults for the entire notebook in one call.
Disable Auto-Preview
To restore the default DataFrame display, call disable_tbl_preview():
This removes the formatter and returns to the library’s built-in HTML rendering.
Parameters Reference
The table below lists every parameter available on the tbl_preview() function. All parameters except data are optional.
data |
various |
— |
Data source: DataFrame, file path, dict, or list of dicts |
columns |
list[str] |
None |
Column names to display (all if None) |
n_head |
int |
5 |
Rows from the start |
n_tail |
int |
5 |
Rows from the end |
limit |
int |
50 |
Maximum allowed n_head + n_tail |
show_all |
bool |
False |
Show all rows |
show_row_numbers |
bool |
True |
Show row-number gutter |
show_dtypes |
bool |
True |
Show dtype labels |
show_dimensions |
bool |
True |
Show header banner |
max_col_width |
int |
250 |
Maximum column width (px) |
min_tbl_width |
int |
500 |
Minimum table width (px) |
caption |
str |
None |
Caption text |
highlight_missing |
bool |
True |
Highlight missing values |
row_index_offset |
int |
0 |
Starting row number (0-based by default) |
id |
str |
auto |
Custom HTML id for the container |
The return value is a TblPreview object with _repr_html_() (for notebook display), as_html() (returns the HTML string), and save(path) (writes HTML to a file) methods.
Next Steps
Table previews give readers a quick look at a dataset’s structure and content without loading the full table. Use them in docstring examples, user guides, or anywhere you want to show data at a glance.
- Table Explorer adds interactive filtering, sorting, and pagination for larger datasets
- Scale-to-Fit keeps wide tables readable on narrow screens
- Writing Docstrings covers embedding table previews in executable docstring examples