Skip to content

Report Features

Beyond basic save_report(), Data In Pane offers several options for customizing the output.

Offline Mode

Bundle all JavaScript and CSS inline for fully self-contained HTML that works without an internet connection:

dip.save_report(view, path="report.html", offline=True)

The first call fetches assets from the CDN and caches them in ~/.cache/datainpane/cdn/. Subsequent calls are instant. Google Fonts are replaced with system font fallbacks.

Table of Contents

Generate a fixed sidebar from Page titles and Group labels:

view = dip.Blocks(
    dip.Page(dip.Text("# Intro"), title="Introduction"),
    dip.Page(dip.DataTable(df), title="Data"),
)
dip.save_report(view, path="report.html", toc=True)

The TOC appears as a fixed sidebar on the left with smooth-scroll navigation.

Add branding to the top and bottom of reports:

fmt = dip.Formatting(
    header="<strong>ACME Corp</strong> — Monthly Report",
    footer="Generated by Data In Pane &middot; Confidential",
)
dip.save_report(view, path="report.html", formatting=fmt)

Headers and footers accept raw HTML, so you can include logos:

header = '<img src="data:image/png;base64,..." height="32"/> My Company'

Dark Mode Toggle

Add a floating button that lets viewers switch between light and dark modes:

fmt = dip.Formatting(dark_mode_toggle=True)
dip.save_report(view, path="report.html", formatting=fmt)

The toggle:

  • Persists the viewer's preference via localStorage
  • Uses CSS variable overrides (not crude filter hacks) for Callout, Progress, and Diff blocks
  • Applies an invert filter to the main report content for broad compatibility

Multi-Page Export

Save each Page as a separate HTML file:

view = dip.Blocks(
    dip.Page(dip.Text("# Introduction"), title="Introduction"),
    dip.Page(dip.DataTable(df), title="Data"),
    dip.Page(dip.Plot(chart), title="Charts"),
)
paths = dip.save_report_pages(view, dest="output/", name="Report")
# Creates: output/Report - Introduction.html
#          output/Report - Data.html
#          output/Report - Charts.html

Report Templates

Pre-built layout templates for common patterns:

import datainpane as dip

# Dashboard: BigNumbers on top, charts/tables in grid below
view = dip.templates.dashboard([
    dip.BigNumber(heading="Users", value="10k"),
    dip.BigNumber(heading="Revenue", value="$1.2M"),
    dip.Plot(chart),
    dip.DataTable(df),
])

# Titled pages: split blocks into pages at # headings
view = dip.templates.titled_pages([
    dip.Text("# Overview"),
    dip.DataTable(df),
    dip.Text("# Analysis"),
    dip.Plot(chart),
])

# Descriptive pages: new page at each text→non-text boundary
view = dip.templates.descriptive_pages([...])

Hiding Altair Action Menus

Remove the "View Source", "Save as PNG", etc. menu from Altair/Vega charts:

dip.Plot(chart, show_actions=False)

Table Cell Limit

By default, DataFrames with more than 250 cells are auto-wrapped as DataTable (interactive, searchable) instead of Table (static HTML). Override:

import datainpane.blocks.wrappers as w
w.TABLE_CELL_LIMIT = 1000

Custom CSS

Inject arbitrary CSS via the custom_css parameter on Formatting:

fmt = dip.Formatting(custom_css="""
    /* Reduce spacing between blocks */
    .group > * { margin-bottom: 4px; }

    /* Remove scrollbar from DataTables */
    [data-cy=block-datatable] { overflow: hidden; max-height: none; }

    /* Limit report height with scroll */
    #report { max-height: 80vh; overflow-y: auto; }
""")

Matplotlib DPI and Cropping

Control matplotlib rendering with scale (DPI multiplier) and savefig_kw:

dip.Plot(fig, scale=2.0)  # 2x DPI for high-res output
dip.Plot(fig, savefig_kw={"dpi": 150, "pad_inches": 0.5})

tight_layout() is called automatically before saving to prevent cropping on multi-axes figures.