# Great Tables: building publication-quality display tables from Pandas and Polars

> Great Tables is a Python library from Posit that composes display tables from named components and renders them to HTML. It targets analysts who need formatted, information-rich tables rather than plain DataFrame output.

**posit-dev/great-tables** — Make awesome display tables using Python

- Repository: https://github.com/posit-dev/great-tables
- Website: https://posit-dev.github.io/great-tables/
- Stars: 2,865 · Forks: 135
- Language: Python
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/posit-dev-great-tables

## What Great Tables does that a DataFrame repr does not

A DataFrame repr is a debugging aid. It shows whatever the frame holds, in whatever unit the column was stored in, with the column order the query produced. Great Tables starts from that same frame and treats the table as a composition problem: a header, a footer, a stub of row labels, spanner labels arranged above the column labels, and per-column formatting. The README describes the philosophy as constructing a wide variety of useful tables "by working with a cohesive set of table components", which is a fair summary of the API surface.

The intended user is someone producing a table that another person will read: an analyst writing a report, a researcher preparing a Quarto document, a data team publishing a weekly figure. The package classifiers list scientific and research, financial and insurance, and healthcare audiences alongside developers, which matches the formatting methods it ships (currency, dates, compact numbers). The output is HTML by default, or an image file.

The scope boundary matters. This is a display library, not a data library. It does not reshape, join or aggregate for you. The README's example filters the bundled sp500 dataset with Pandas before the table object is constructed, and that ordering is the intended pattern.

## The composition model: GT objects, column formatting and rendering

The entry point is the GT class, constructed from a Pandas or Polars DataFrame. Every subsequent call returns a new table object, so the chain is a declarative description of the final table rather than a sequence of mutations. The README's example reads as a single expression: tab_header sets the title and subtitle, fmt_currency formats the open, high, low and close columns, fmt_date formats the date column with the named style wd_m_day_year, fmt_number renders volume in compact form, and cols_hide drops adj_close from the output without touching the underlying frame.

That last method is the clearest illustration of the data flow. The frame keeps its columns; the table object decides which of them reach the reader. Rendering happens at the end of the chain, to HTML by default or to an image file. The repository carries a js directory under great_tables with JavaScript and CSS files declared as package data in pyproject.toml, so the HTML output is not bare markup: it ships its own styling assets.

The package also bundles 16 datasets, including countrypops, sza, gtcars, sp500, pizzaplace, exibble, towny, peeps, films, metro, gibraltar, constants, illness, reactions, photolysis and nuclides. The README states these are used extensively in the documentation, which means any example you copy from the docs will run without you supplying data first.

## Installing Great Tables and rendering a first table

The README gives the PyPI install as a single command. The README also points to Conda-Forge as an alternative channel, with the package listed there as great_tables, though the truncated text does not spell out the conda command.

```bash
$ pip install great_tables
```

The package requires Python 3.10 or newer, and its classifiers list 3.10 through 3.14 plus PyPy. Core dependencies include htmltools, Babel, faicons, multimark, nokap and typing_extensions. Pandas and Polars appear in the optional dependency groups rather than the core list, so install the one you actually use.

A first real use is the README's own sp500 example. It filters the bundled dataset to a week of rows, then builds and formats the table. Copy it as-is and you should see a titled table with currency-formatted price columns, a human-readable date column and a compact volume figure.

```python
from great_tables import GT
from great_tables.data import sp500

start_date = "2010-06-07"
end_date = "2010-06-14"

sp500_mini = sp500[(sp500["date"] >= start_date) & (sp500["date"] <= end_date)]

(
    GT(sp500_mini)
    .tab_header(title="S&P 500", subtitle=f"{start_date} to {end_date}")
    .fmt_currency(columns=["open", "high", "low", "close"])
    .fmt_date(columns="date", date_style="wd_m_day_year")
    .fmt_number(columns="volume", compact=True)
    .cols_hide(columns="adj_close")
)
```

Where you run this changes what you see. The README states that the library is typically used in a notebook environment or within a Quarto document, and that tables will not print to the console. If you are in a plain terminal, calling show() on the table object opens the HTML table in your default browser instead.

## Where Great Tables stops being the right tool

The console is the first hard boundary. The README states plainly that tables will not print to the console, so any script whose output is a terminal log gets nothing useful from the default rendering path. If your workflow is a CLI report or a cron job that writes to stdout, this is the wrong layer.

The second boundary is the input format. Table data must arrive as a Pandas or Polars DataFrame. NumPy arrays, lists of dicts, database cursors and Arrow tables are not the stated input, so you convert first. That is a small cost, but it means the library sits downstream of your data layer rather than replacing any part of it.

The third is the rendering target. HTML is the default and an image file is the stated alternative. The README does not document PDF or LaTeX output, and it does not document a rollback or undo step for a chain of formatting calls, so a long chain is rebuilt rather than unwound. If your deliverable is a Word document or a typeset PDF, you are relying on something the README does not describe.

Finally, the styling assets ship with the package. If you are dropping the HTML into a page with a strict content security policy or a CSS reset, the bundled JavaScript and CSS files under great_tables/js are the things to inspect first.

## Great Tables compared with a plotting or reporting library

The natural alternative is a plotting library such as Matplotlib, where a table is drawn onto a figure canvas. The difference in approach is structural. In a plotting library the table is a graphical object positioned in axes coordinates: you place text at x and y, and column widths are your problem. In Great Tables the table is a document with named regions, and the library owns layout. Formatting is declared per column through methods like fmt_currency and fmt_date rather than applied to strings you built yourself.

That makes Great Tables better suited to tables that are read as tables, and worse suited to figures where the table is one element among several in a composed image. A Matplotlib table can sit next to a chart in the same figure; a Great Tables object renders on its own.

A second alternative is writing HTML by hand or with a templating engine. That gives total control and no dependency, at the cost of reimplementing currency and date formatting, column hiding and header structure for every table. Great Tables is essentially a named, tested version of that work. The trade is that you adopt its component vocabulary, and the README is explicit that the vocabulary is the point: header, footer, stub, spanner labels, column labels.

## Maintenance, releases and the MIT licence

The repository is not archived, and the last push was on 2026-09-24, four days before this writing. The release history shows v0.24.0 on 2026-08-24, v0.23.0 on 2026-07-27 and v0.22.0 on 2026-06-12, a cadence of roughly one minor release a month across the summer. The version is dynamic, driven by setuptools_scm from git tags, so there is no version string to edit in pyproject.toml.

Upgrade cost is bounded by the pre-1.0 version number. Minor releases can carry behaviour changes, and the Makefile exposes a snapshot-update target that runs pytest with --snapshot-update, which tells you the test suite relies on snapshot comparisons of rendered output. That is good for catching regressions and also means rendering details can shift between releases. The Makefile's check target runs pyright against Python 3.8 through 3.11, while the package itself declares requires-python >=3.10, so the type-checking matrix is wider than the supported runtime range.

Licensing is MIT, declared in pyproject.toml as license.file pointing at the LICENSE file and confirmed by the OSI classifier. MIT is permissive: it allows commercial and closed-source use with attribution and no warranty. The bundled datasets are the thing to check separately if you redistribute them, since the repository licence and the provenance of individual datasets are different questions. That is a question for your own review, not a conclusion this article can draw.

## Conclusion

Adopt Great Tables when the deliverable is a rendered table with a title, formatted numbers and a controlled column set, and you are working in a notebook, a Quarto document or any pipeline that can carry HTML. Do not adopt it as a console pretty-printer: the README states tables will not print to the console, and the fallback is the show() method opening a browser. Before committing, verify that your pinned Pandas or Polars version is supported, since the package declares requires-python >=3.10 and carries pandas and polars in its optional dependency groups, and confirm the rendering path your target actually consumes.

## FAQ

### How do I install Great Tables in Python?

The README gives the install as pip install great_tables from PyPI, and also points to Conda-Forge as an alternative channel. The package requires Python 3.10 or newer.

### Does Great Tables work with Polars DataFrames?

Yes. The README states that table data begins as a Pandas or Polars DataFrame, and the repository topics list both pandas-dataframe and polars-dataframe. Polars appears in the optional dependency groups rather than the core dependency list.

### How do I save or render a Great Tables table to HTML?

HTML is the default rendering target, with an image file as the stated alternative. In a console session the README says to call the show() method on the table object, which opens the HTML table in your default browser.

### Can I use Great Tables inside a Quarto document?

The README states that Great Tables is typically used in a notebook environment or within a Quarto document. That is the documented usage pattern rather than a separate integration.

### What are the three types of tables?

This question is not about Great Tables, so it is out of scope here. The library's own framing is a set of table components such as header, footer, stub, spanner labels and column labels.

## Sources

- [License: MIT](https://github.com/posit-dev/great-tables/blob/main/LICENSE)
- [posit-dev/great-tables on GitHub](https://github.com/posit-dev/great-tables)
- [Project website](https://posit-dev.github.io/great-tables/)
- [README](https://github.com/posit-dev/great-tables/blob/main/README.md)
- [Releases](https://github.com/posit-dev/great-tables/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/posit-dev-great-tables
