Reflex wrapper for the MUI X DataGrid (v8) React component with polars LazyFrame support
Project description
reflex-mui-datagrid
Reflex wrapper for the MUI X DataGrid (v8) React component, with built-in polars LazyFrame support and optional genomic data visualization via polars-bio.
Installation
uv add reflex-mui-datagrid
For CLI usage, you can run the tool as biogrid (see CLI section below).
For genomic data support (VCF/BAM files), install with the [bio] extra:
uv add "reflex-mui-datagrid[bio]"
Requires Python >= 3.12, Reflex >= 0.8.26, and polars >= 1.0.
CLI VCF Viewer (No Boilerplate)
The package includes a CLI entrypoint that can launch an interactive viewer for VCF and other tabular formats. This is the fastest way to explore a VCF without writing any app code.
Install as a global uv tool (with genomic support):
uv tool install "reflex-mui-datagrid[bio]"
This installs both commands:
reflex-mui-datagrid(full name)biogrid(bio-focused alias)
Open a VCF in your browser:
reflex-mui-datagrid path/to/variants.vcf
# bio-focused alias
biogrid path/to/variants.vcf
Useful options:
reflex-mui-datagrid path/to/variants.vcf --limit 5000 --port 3005 --title "Tumor Cohort VCF"
# bio-focused alias
biogrid path/to/variants.vcf --limit 5000 --port 3005 --title "Tumor Cohort VCF"
The CLI auto-detects file formats by extension and currently supports:
- Genomics (via
polars-bio):vcf,bam,gff,bed,fasta,fastq - Tabular:
csv,tsv,parquet,json,ndjson,ipc/arrow/feather
Quick Start
The fastest way to visualize a polars DataFrame or LazyFrame is the show_dataframe helper:
import polars as pl
import reflex as rx
from reflex_mui_datagrid import show_dataframe
df = pl.read_csv("my_data.csv")
def index() -> rx.Component:
return show_dataframe(df, height="500px")
app = rx.App()
app.add_page(index)
That single call handles column type detection, dropdown filters for low-cardinality columns, row IDs, JSON serialization, and the MUI toolbar -- all automatically.
With State (for reactive updates)
For grids that update in response to user actions, use lazyframe_to_datagrid inside a rx.State event handler:
import polars as pl
import reflex as rx
from reflex_mui_datagrid import data_grid, lazyframe_to_datagrid
class State(rx.State):
rows: list[dict] = []
columns: list[dict] = []
def load_data(self) -> None:
lf = pl.LazyFrame({
"id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
"score": [95, 82, 91],
})
self.rows, col_defs = lazyframe_to_datagrid(lf)
self.columns = [c.dict() for c in col_defs]
def index() -> rx.Component:
return data_grid(
rows=State.rows,
columns=State.columns,
show_toolbar=True,
height="400px",
)
app = rx.App()
app.add_page(index, on_load=State.load_data)
Core Features
- MUI X DataGrid v8 (Community edition, MIT) with
@mui/materialv7 - No pagination by default -- all rows are scrollable; MUI's built-in row virtualisation only renders visible DOM rows, keeping scrolling smooth for large datasets
- No 100-row limit -- the Community edition's artificial page-size cap is removed via a small JS patch; pass
pagination=Trueto re-enable pagination with any page size show_dataframe()helper -- one-liner to turn any polars DataFrame or LazyFrame into a fully-featured interactive grid- Polars LazyFrame integration --
lazyframe_to_datagrid()converts any LazyFrame to DataGrid-ready rows and column definitions in one call - Automatic column type detection -- polars dtypes map to DataGrid types (
number,boolean,date,dateTime,string) - Automatic dropdown filters -- low-cardinality string columns and
Categorical/Enumdtypes becomesingleSelectcolumns with dropdown filters - JSON-safe serialization -- temporal columns become ISO strings,
Listcolumns become comma-joined strings,Structcolumns become strings ColumnDefmodel with snake_case Python attrs that auto-convert to camelCase JS props- Event handlers for row click, cell click, sorting, filtering, pagination, and row selection
- Auto-sized container --
WrappedDataGridwraps the grid in a<div>with configurablewidth/height - Row identification --
row_id_fieldparameter for custom row ID, auto-generated__row_id__column when noidcolumn exists
The show_dataframe Helper
show_dataframe is designed for polars users who want to quickly visualize a DataFrame without wiring up Reflex state. It accepts a pl.DataFrame or pl.LazyFrame and returns a ready-to-render component:
from reflex_mui_datagrid import show_dataframe
# Basic usage -- just pass a DataFrame
grid = show_dataframe(df)
# With options
grid = show_dataframe(
df,
height="600px",
density="compact",
show_toolbar=True,
limit=1000, # collect at most 1000 rows
column_descriptions={"score": "Final exam score (0-100)"},
show_description_in_header=True, # show descriptions as subtitles
column_header_height=70, # taller headers for subtitles
)
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
data |
LazyFrame | DataFrame |
required | The polars data to visualize |
height |
str |
"600px" |
CSS height of the grid container |
width |
str |
"100%" |
CSS width of the grid container |
show_toolbar |
bool |
True |
Show MUI toolbar (columns, filters, density, export) |
density |
str | None |
None |
"comfortable", "compact", or "standard" |
limit |
int | None |
None |
Max rows to collect from LazyFrame |
column_descriptions |
dict | None |
None |
{column: description} for header tooltips |
show_description_in_header |
bool |
False |
Show descriptions as subtitles in headers |
column_header_height |
int | None |
None |
Header height in px (useful with description subtitles) |
checkbox_selection |
bool |
False |
Show checkbox column for row selection |
on_row_click |
EventHandler | None |
None |
Handler called when a row is clicked |
When to use show_dataframe vs lazyframe_to_datagrid:
- Use
show_dataframefor quick prototyping, static dashboards, or when you just want to see your data. - Use
lazyframe_to_datagridinsiderx.Statewhen the grid data needs to change in response to user actions (filtering server-side, loading different files, etc.).
Genomic Data Visualization
polars-bio is a bioinformatics library that reads genomic file formats (VCF, BAM, GFF, FASTA, FASTQ, and more) as native polars LazyFrames. Since show_dataframe accepts any polars LazyFrame, you get an interactive genomic data browser in two lines of code -- no boilerplate needed.
Extra Dependencies
Install with the [bio] extra to pull in polars-bio:
uv add "reflex-mui-datagrid[bio]"
This adds polars-bio >= 0.23.0, which provides scan_vcf(), scan_bam(), scan_gff(), and other genomic file readers -- all returning standard polars LazyFrames.
If you only want quick interactive exploration, the CLI is the simplest option:
reflex-mui-datagrid variants.vcf
Quick VCF Visualization (two lines)
Because polars_bio.scan_vcf() returns a polars LazyFrame, you can pass it straight to show_dataframe:
import polars_bio as pb
from reflex_mui_datagrid import show_dataframe
lf = pb.scan_vcf("variants.vcf") # polars LazyFrame
def index() -> rx.Component:
return show_dataframe(lf, density="compact", height="540px")
That is all you need -- column types, dropdown filters for low-cardinality fields like filter and genotype, row IDs, and the MUI toolbar are all set up automatically.
VCF with Column Descriptions from Headers
For richer display, bio_lazyframe_to_datagrid automatically extracts column descriptions from VCF INFO/FORMAT headers and shows them as tooltips or subtitles in the column headers:
import polars_bio as pb
import reflex as rx
from reflex_mui_datagrid import bio_lazyframe_to_datagrid, data_grid
class State(rx.State):
rows: list[dict] = []
columns: list[dict] = []
def load_vcf(self) -> None:
lf = pb.scan_vcf("variants.vcf")
self.rows, col_defs = bio_lazyframe_to_datagrid(lf)
self.columns = [c.dict() for c in col_defs]
def index() -> rx.Component:
return data_grid(
rows=State.rows,
columns=State.columns,
show_toolbar=True,
show_description_in_header=True, # VCF descriptions as subtitles
density="compact",
column_header_height=70,
height="540px",
)
app = rx.App()
app.add_page(index, on_load=State.load_vcf)
bio_lazyframe_to_datagrid merges three sources of column descriptions:
- VCF specification -- standard fields (chrom, start, ref, alt, qual, filter, etc.)
- INFO fields -- descriptions from the file's
##INFOheader lines - FORMAT fields -- descriptions from the file's
##FORMATheader lines
Server-Side Scroll-Loading (Large Datasets)
For datasets too large to load into the browser at once (millions of rows), the LazyFrameGridMixin provides a complete server-side solution with scroll-driven infinite loading, filtering, and sorting -- all backed by a polars LazyFrame that is never fully collected into memory.
Quick Example
from pathlib import Path
from reflex_mui_datagrid import LazyFrameGridMixin, lazyframe_grid, scan_file
class MyState(LazyFrameGridMixin):
def load_data(self):
lf, descriptions = scan_file(Path("my_genome.vcf"))
yield from self.set_lazyframe(lf, descriptions)
def index() -> rx.Component:
return rx.box(
rx.button("Load", on_click=MyState.load_data, loading=MyState.lf_grid_loading),
rx.cond(MyState.lf_grid_loaded, lazyframe_grid(MyState)),
)
That's it -- you get server-side filtering, sorting, and infinite scroll-loading with no additional wiring.
scan_file -- Auto-Detect File Format
scan_file opens any supported file as a polars LazyFrame and extracts column descriptions where available:
from reflex_mui_datagrid import scan_file
# VCF -- auto-extracts column descriptions from headers
lf, descriptions = scan_file(Path("variants.vcf"))
# Parquet -- no descriptions, but LazyFrame is ready
lf, descriptions = scan_file(Path("data.parquet"))
# Also supports: .csv, .tsv, .json, .ndjson, .ipc, .arrow, .feather
LazyFrameGridMixin -- State Mixin
LazyFrameGridMixin extends rx.State and provides all the state variables and event handlers needed for server-side browsing. Inherit from it in your state class:
class MyState(LazyFrameGridMixin):
# Your own state vars
file_available: bool = False
def load_data(self):
lf, descriptions = scan_file(Path("data.parquet"))
yield from self.set_lazyframe(lf, descriptions, chunk_size=500)
State variables (all prefixed lf_grid_ to avoid collisions):
| Variable | Type | Description |
|---|---|---|
lf_grid_rows |
list[dict] |
Currently loaded rows |
lf_grid_columns |
list[dict] |
Column definitions |
lf_grid_row_count |
int |
Total rows matching current filter |
lf_grid_loading |
bool |
Loading indicator |
lf_grid_loaded |
bool |
Whether data has been loaded |
lf_grid_stats |
str |
Last refresh timing info |
lf_grid_selected_info |
str |
Detail string for clicked row |
set_lazyframe parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
lf |
pl.LazyFrame |
required | The LazyFrame to browse |
descriptions |
dict[str, str] | None |
None |
Column descriptions for tooltips |
chunk_size |
int |
200 |
Rows per scroll chunk |
value_options_max_unique |
int |
500 |
Max distinct values for dropdown filter (queried from full dataset) |
lazyframe_grid -- Pre-Wired UI Component
Returns a data_grid(...) with all server-side handlers already connected:
from reflex_mui_datagrid import lazyframe_grid, lazyframe_grid_stats_bar, lazyframe_grid_detail_box
def my_page() -> rx.Component:
return rx.fragment(
lazyframe_grid_stats_bar(MyState), # row count + timing bar
lazyframe_grid(MyState, height="600px"),
lazyframe_grid_detail_box(MyState), # clicked row details
)
lazyframe_grid parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
state_cls |
type |
required | Your state class inheriting LazyFrameGridMixin |
height |
str |
"600px" |
CSS height |
width |
str |
"100%" |
CSS width |
density |
str |
"compact" |
Grid density |
column_header_height |
int |
70 |
Header height in px |
scroll_end_threshold |
int |
260 |
Pixels from bottom to trigger next chunk |
show_toolbar |
bool |
True |
Show MUI toolbar |
show_description_in_header |
bool |
True |
Show column descriptions as subtitles |
debug_log |
bool |
True |
Browser console debug logging |
on_row_click |
EventHandler | None |
None |
Override default row-click handler |
How It Works
set_lazyframestores the LazyFrame in a module-level cache (never serialised into Reflex state), computes the schema, total row count, and low-cardinality filter options from a bounded sample.- Only the first chunk of rows is collected and sent to the frontend.
- As the user scrolls near the bottom,
handle_lf_grid_scroll_endcollects the next chunk and appends it. - Filter and sort changes reset to page 0 and re-query the LazyFrame with Polars expressions -- no full-table collect.
Running the Example
The project uses uv workspaces. The example app is a workspace member with a demo entrypoint:
uv sync
uv run demo
The demo has three tabs:
| Tab | Description |
|---|---|
| Employee Data | 20-row inline polars LazyFrame with sorting, dropdown filters, checkbox selection |
| Genomic Variants (VCF) | 793 variants loaded via polars_bio.scan_vcf(), column descriptions from VCF headers |
| Full Genome (Server-Side) | ~4.5M variants with server-side scroll-loading, filtering, and sorting via LazyFrameGridMixin |
API Reference
See docs/api.md for the full API reference.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reflex_mui_datagrid-0.1.5.tar.gz.
File metadata
- Download URL: reflex_mui_datagrid-0.1.5.tar.gz
- Upload date:
- Size: 33.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81336ba10e7e80f5f2dce38a5f471636a891763550700eda819f67b7e05b8807
|
|
| MD5 |
e69c2f0e78cacd95905d64a3ea483725
|
|
| BLAKE2b-256 |
022e6a8f9086f33119e77f7767fe9339b19f207542ec264d09956cb0c1618c57
|
File details
Details for the file reflex_mui_datagrid-0.1.5-py3-none-any.whl.
File metadata
- Download URL: reflex_mui_datagrid-0.1.5-py3-none-any.whl
- Upload date:
- Size: 39.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61043984226acd42bfcac6148dad7fbeda3a460612ce8b29998ca9251dd6db0e
|
|
| MD5 |
4c8ba93d92786ef3391a227dd4a4aea9
|
|
| BLAKE2b-256 |
d1318d60245cffa6eb8931e8e71f5bbe73fa1ddeb44379cd0a5d09e385b6df43
|