Skip to content

[feature] Add hide_index and hide_header to st.table#14113

Merged
lukasmasuch merged 6 commits intodevelopfrom
feature/table-hide-index-hide-header
Mar 10, 2026
Merged

[feature] Add hide_index and hide_header to st.table#14113
lukasmasuch merged 6 commits intodevelopfrom
feature/table-hide-index-hide-header

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Feb 24, 2026

Describe your changes

Adds hide_index and hide_header parameters to st.table() with intelligent auto-hide behavior:

  • hide_index: Auto-hides default RangeIndex (0, 1, 2...) while showing custom indices
  • hide_header: Auto-hides headers for data without user-defined column names (dict, list, numpy arrays)

Both parameters accept True/False to explicitly control visibility, or None (default) for auto-hide behavior.

Github Issues

Testing Plan

  • Unit Tests (JS and/or Python)
    • lib/tests/streamlit/elements/table_test.py — Tests auto-hide logic, explicit values, Styler support
    • frontend/lib/src/components/elements/Table/Table.test.tsx — Tests frontend rendering with hideIndex/hideHeader
  • E2E tests
    • e2e_playwright/st_table_test.py — Visual snapshot tests for all hide_index/hide_header scenarios

Copilot AI review requested due to automatic review settings February 24, 2026 19:13
@lukasmasuch lukasmasuch added change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users labels Feb 24, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 24, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 24, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-14113/streamlit-1.55.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-14113.streamlit.app (☁️ Deploy here if not accessible)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds hide_index and hide_header parameters to st.table() with intelligent auto-hide behavior. When set to None (default), hide_index auto-hides default RangeIndex (0, 1, 2...) while showing custom indices, and hide_header auto-hides headers for data formats without user-defined column names (dict, list, numpy arrays). Both parameters accept explicit True/False values to override the auto-hide behavior.

Changes:

  • Added two boolean protobuf fields to the Table message for hide_index and hide_header
  • Implemented backend logic for auto-detection of RangeIndex and simple data formats
  • Updated frontend rendering to conditionally hide index columns and header rows
  • Added comprehensive unit tests (Python and TypeScript) and E2E visual snapshot tests

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
proto/streamlit/proto/Table.proto Added hide_index and hide_header boolean fields (3 and 4) to Table message
lib/streamlit/elements/table.py Added _compute_hide_index and _compute_hide_header helper functions, parameter handling, and auto-hide logic based on data format
lib/tests/streamlit/elements/table_test.py Added comprehensive unit tests for auto-hide logic, explicit values, Styler support, and combined scenarios
frontend/lib/src/components/elements/Table/Table.tsx Implemented conditional rendering of index columns and headers, updated sticky positioning logic
frontend/lib/src/components/elements/Table/Table.test.tsx Added unit tests verifying hideIndex and hideHeader rendering with positive and negative assertions
e2e_playwright/st_table.py Added test app with 10 new table examples covering all hide_index/hide_header scenarios
e2e_playwright/st_table_test.py Added 10 E2E tests with visual snapshots and DOM assertions for the new parameters

@lukasmasuch lukasmasuch added the update-snapshots Trigger snapshot autofix workflow label Feb 24, 2026
@github-actions github-actions bot removed the update-snapshots Trigger snapshot autofix workflow label Feb 24, 2026
lukasmasuch and others added 2 commits February 24, 2026 20:39
Use the more idiomatic assert_called_once() assertion method instead of
manually checking call_count == 1, as suggested in code review.
## Describe your changes

Automated snapshot updates for #14113 created via the snapshot autofix
CI workflow.

This workflow was triggered by adding the `update-snapshots` label to a
PR after Playwright E2E tests failed with snapshot mismatches.

**Updated snapshots:** 182 file(s)

⚠️ **Please review the snapshot changes carefully** - they could mask
visual bugs if accepted blindly.

This PR targets a feature branch and can be merged without review
approval.

Co-authored-by: Streamlit Bot <[email protected]>
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Feb 24, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Feb 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Summary

This PR adds two new parameters — hide_index and hide_header — to st.table(). Both accept bool | None:

  • hide_index: When None (default), auto-hides the index if it's a default RangeIndex; shows custom indices. Explicit True/False overrides.
  • hide_header: When None (default), auto-hides column headers for data formats without user-defined column names (dict, list, numpy, set, tuple, etc.). Explicit True/False overrides.

Changes span the full stack: protobuf schema (Table.proto), Python backend (table.py), React frontend (Table.tsx), and comprehensive tests (Python unit, frontend unit, and E2E with snapshots).

Code Quality

The implementation is clean and follows existing patterns well.

Backend (lib/streamlit/elements/table.py):

  • _compute_hide_index and _compute_hide_header are well-structured module-level functions with proper docstrings, type annotations, and from __future__ import annotations.
  • The _HIDE_HEADER_DATA_FORMATS set correctly identifies data formats without user-defined column names. The selection (KEY_VALUE_DICT, LIST_OF_ROWS, LIST_OF_VALUES, NUMPY_LIST, NUMPY_MATRIX, SET_OF_VALUES, TUPLE_OF_VALUES, PANDAS_ARRAY, PYARROW_ARRAY) is well-considered — formats with explicit column names (COLUMN_VALUE_MAPPING, LIST_OF_RECORDS, PANDAS_DATAFRAME, etc.) are correctly excluded.
  • Smart avoidance of double DataFrame conversion via the converted_df parameter — the pre-converted DataFrame from is_unevaluated_data_object handling is reused in _compute_hide_index.
  • Good handling of Styler objects by accessing data.data to inspect the underlying DataFrame's index.
  • The data_format is determined before the unevaluated data conversion, which is correct since determine_data_format inspects the original input type.

Frontend (frontend/lib/src/components/elements/Table/Table.tsx):

  • hideIndex and hideHeader are derived during render (not in effects), following React best practices per the AGENTS.md.
  • Index columns are filtered at render time by returning null from map() in both generateTableHeader and generateTableRow — the underlying Quiver data is untouched.
  • visibleNumColumns is correctly recalculated for the empty table cell's colSpan.
  • Sticky header/index logic correctly accounts for hideHeader and hideIndex (e.g., enableStickyHeaders is false when hideHeader is true).

Protobuf (proto/streamlit/proto/Table.proto):

  • Two new bool fields at field numbers 3 and 4 — clean additions, no conflicts.

Docstrings: The new parameter docstrings follow the NumPy style, use double backticks for inline code, and list options with bullet points — consistent with existing parameter documentation. The two new examples (4 and 5) are useful but lack .. output:: directives. This is minor since Example 3 also lacks one, so it's consistent within the file.

Test Coverage

Test coverage is thorough across all layers:

Python unit tests (lib/tests/streamlit/elements/table_test.py):

  • Explicit True/False for both parameters (parameterized).
  • Auto-hide logic for RangeIndex vs. custom index.
  • Auto-hide logic for DataFrames vs. simple data formats (dict, list, nested list, numpy).
  • Styler object support (with both RangeIndex and custom index).
  • Explicit override of auto-hide behavior.
  • Combined hide_index=True + hide_header=True.

Frontend unit tests (Table.test.tsx):

  • Hide/show index columns with hideIndex true/false.
  • Hide/show header row with hideHeader true/false.
  • Anti-regression checks (data cells still present when index/header hidden; thead still present when hideHeader is false).

E2E tests (e2e_playwright/st_table_test.py):

  • 10 scenario-specific tests with both behavioral assertions and visual snapshots.
  • Covers: auto-hide RangeIndex, custom index shown, explicit true/false for both params, auto-hide for dict/list data, combined params, and MultiIndex with hide_index.

Minor observations on E2E tests:

  • Per the E2E AGENTS.md guidance to "prefer a single aggregated scenario test over many micro-tests when they share the same setup and page state," the 10 separate test functions could be consolidated into 2–3 aggregated tests (e.g., one for hide_index scenarios, one for hide_header scenarios) to reduce browser loads. This is a style preference, though, and the current approach is clearer for debugging failures.
  • Tests rely on nth() index-based access (indices 37–46), which is fragile if tables are added/removed earlier in the app. This is consistent with the existing test style in this file, but wrapping with st.container(key=...) would be more resilient.

Missing but optional test coverage:

  • No typing test file (lib/tests/streamlit/typing/table_types.py) was added for the new parameters. This is pre-existing (there was none before), but would be valuable for catching typing regressions on the public API.
  • No test for hide_index=True on an empty DataFrame (0 rows).

Backwards Compatibility

This is a behavioral breaking change for existing users. With the default None values:

  1. Index auto-hide: Any st.table(df) where df has a default RangeIndex (the common case for most DataFrames) will now hide the index column. Previously, the numeric index (0, 1, 2, ...) was always visible.
  2. Header auto-hide: Any st.table(dict_or_list) will now hide column headers. Previously, auto-generated headers like "0" were always visible.

This is reflected in the updated E2E snapshots — many existing numbered snapshots (st_table-0 through st_table-13, st_table-27 through st_table-37) were updated because the visual appearance changed.

Users can opt out with hide_index=False and/or hide_header=False. The change is labeled change:feature and impact:users, indicating it's intentional.

Wire protocol compatibility is preserved:

  • Old frontends receiving new proto fields will ignore unknown fields (proto3 behavior) and continue showing everything.
  • New frontends receiving old protos will see false for both bools (proto3 default), correctly showing index and headers.

Security & Risk

No security concerns identified. The changes are purely display-related:

  • No user input is used in unsafe ways.
  • No new endpoints or authentication changes.
  • No filesystem or network operations added.

Regression risk is low-to-moderate:

  • The auto-hide logic could produce unexpected behavior for edge-case data types not covered in tests (e.g., Polars DataFrames, Modin DataFrames, Snowpark DataFrames passed to _compute_hide_index). These go through the final convert_anything_to_pandas_df fallback path in _compute_hide_index, which should work correctly since the conversion produces a DataFrame with a RangeIndex by default.

Accessibility

  • When hideHeader is true, the <thead> element is not rendered, which means screen readers lose the column header context for table cells. This is an inherent trade-off of hiding headers, and is the expected behavior when users explicitly choose to hide them.
  • When hideIndex is true, <th scope="row"> elements are removed, reducing row-level semantic information. Again, this is the expected outcome of the user's choice.
  • The existing accessibility attributes for scrollable tables (role="region", tabIndex, aria-label) are correctly preserved and unaffected by these changes.
  • No new interactive elements are introduced, so no new keyboard/focus concerns.

Recommendations

  1. Consider adding a typing test (lib/tests/streamlit/typing/table_types.py) to verify the hide_index and hide_header parameter types are correctly exposed in the public API. This catches type signature regressions via mypy.

  2. E2E test consolidation: Consider consolidating the 10 new E2E test functions into 2–3 aggregated scenario tests (one for hide_index scenarios, one for hide_header scenarios, one for combined). This follows the E2E AGENTS.md guidance to reduce browser loads while maintaining the same coverage.

  3. Document the behavioral change: Since existing tables will look different after this change (auto-hidden index/headers), consider noting this in release notes or a migration guide. Users who relied on seeing the RangeIndex or auto-generated headers will need to add hide_index=False or hide_header=False.

  4. Minor: The new docstring examples 4 and 5 could benefit from .. output:: directives with demo app URLs for consistency with examples 1 and 2.

Verdict

APPROVED: Well-implemented feature with clean code, thorough test coverage across all layers, and correct protobuf wire compatibility. The auto-hide behavioral change is intentional and users can opt out. Minor improvements (typing tests, E2E consolidation, release notes) are recommended but not blocking.


This is an automated AI review by opus-4.6-thinking.

@github-actions
Copy link
Copy Markdown
Contributor

Summary

This PR adds two new parameters — hide_index and hide_header — to st.table(). Both accept bool | None:

  • hide_index: When None (default), auto-hides the index if it's a default RangeIndex; shows custom indices. Explicit True/False overrides.
  • hide_header: When None (default), auto-hides column headers for data formats without user-defined column names (dict, list, numpy, set, tuple, etc.). Explicit True/False overrides.

Changes span the full stack: protobuf schema (Table.proto), Python backend (table.py), React frontend (Table.tsx), and comprehensive tests (Python unit, frontend unit, and E2E with snapshots).

Both reviewers agreed this is a well-structured, cleanly implemented feature. One reviewer (gpt-5.3-codex-high) raised a critical concern about double-consumption of one-shot data sources that was verified to be incorrect upon code inspection (see Code Quality section below).

Code Quality

The implementation is clean and follows existing patterns well across all layers.

Backend (lib/streamlit/elements/table.py):

  • _compute_hide_index and _compute_hide_header are well-structured module-level functions with proper docstrings and type annotations.
  • The _HIDE_HEADER_DATA_FORMATS set correctly identifies data formats without user-defined column names. The selection is well-considered — formats with explicit column names (COLUMN_VALUE_MAPPING, LIST_OF_RECORDS, PANDAS_DATAFRAME, etc.) are correctly excluded.
  • Good handling of Styler objects by accessing data.data to inspect the underlying DataFrame's index.
  • The data_format is determined before the unevaluated data conversion, which is correct since determine_data_format inspects the original input type.

Resolved disagreement — double-consumption claim: gpt-5.3-codex-high flagged a merge-blocking regression claiming one-shot data sources (generators, DB cursors) are consumed twice. After tracing the code, this is not a real issue. The flow for one-shot inputs is:

  1. is_unevaluated_data_object(data) returns True for generators, DB cursors, Snowpark, PySpark, etc. (line 348)
  2. Data is converted once to converted_df, and data is reassigned to this DataFrame (lines 349-352)
  3. _compute_hide_index(data, converted_df, hide_index) receives the already-converted DataFrame as both data and data_df (line 355)
  4. Inside _compute_hide_index, the data_df is not None check (line 130) takes the early-return path, never reaching the fallback convert_anything_to_pandas_df at line 138
  5. marshall_table at line 373 receives the already-converted DataFrame

The fallback path at line 138 only executes for non-DataFrame, non-Styler, non-unevaluated data (plain lists, dicts, numpy arrays, etc.) — none of which are one-shot sources. The converted_df parameter was specifically designed to prevent double conversion.

Frontend (Table.tsx):

  • hideIndex and hideHeader are derived during render (not in effects), following React best practices.
  • Index columns are filtered at render time by returning null from map() — the underlying Quiver data is untouched.
  • visibleNumColumns is correctly recalculated for the empty table cell's colSpan.
  • Sticky header/index logic correctly accounts for hideHeader and hideIndex.

Protobuf (Table.proto):

  • Two new bool fields at field numbers 3 and 4 — clean additive additions, no conflicts.

Docstrings: The new parameter docstrings follow the NumPy style, use double backticks for inline code, and list options with bullet points — consistent with existing documentation. The two new examples (4 and 5) lack .. output:: directives, but this is consistent with Example 3 in the same file. (Both reviewers agreed this is minor.)

Test Coverage

Both reviewers agreed test coverage is thorough. Coverage spans all layers:

Python unit tests (table_test.py):

  • Explicit True/False for both parameters (parameterized).
  • Auto-hide logic for RangeIndex vs. custom index.
  • Auto-hide logic for DataFrames vs. simple data formats (dict, list, nested list, numpy).
  • Styler object support (with both RangeIndex and custom index).
  • Explicit override of auto-hide behavior.
  • Combined hide_index=True + hide_header=True.

Frontend unit tests (Table.test.tsx):

  • Hide/show index columns with hideIndex true/false.
  • Hide/show header row with hideHeader true/false.
  • Anti-regression checks (data cells still present when index/header hidden; thead present when hideHeader is false).

E2E tests (st_table_test.py):

  • 10 scenario-specific tests with behavioral assertions and visual snapshots.
  • Covers: auto-hide RangeIndex, custom index shown, explicit true/false for both params, auto-hide for dict/list data, combined params, and MultiIndex with hide_index.

Minor gaps (non-blocking):

  • No typing test file for the new parameters (pre-existing gap — none existed before).
  • No test for hide_index=True on an empty DataFrame.
  • E2E tests rely on nth() index-based access, which is fragile but consistent with existing test style in the file.

Backwards Compatibility

Both reviewers agreed the wire protocol is compatible:

  • Old frontends will ignore unknown proto fields (proto3 behavior) and continue showing everything.
  • New frontends receiving old protos will see false for both bools (proto3 default), correctly showing index and headers.

This is an intentional behavioral change for existing users. With the default None values:

  1. st.table(df) where df has a default RangeIndex will now hide the index column.
  2. st.table(dict_or_list) will now hide column headers.

Users can opt out with hide_index=False and/or hide_header=False. The change is labeled change:feature and impact:users, indicating it's intentional. Updated E2E snapshots confirm the visual changes.

Security & Risk

Both reviewers agreed: no security concerns. Changes are purely display-related with no new endpoints, authentication changes, or unsafe input handling.

Regression risk is low. The auto-hide logic for edge-case data types (Polars, Modin, Snowpark) goes through the is_unevaluated_data_object path, which correctly converts once and reuses the result.

Accessibility

Both reviewers agreed on the accessibility implications:

  • hideHeader=true removes <thead>, reducing screen-reader column context. This is the expected behavior for an explicit hide option.
  • hideIndex=true removes <th scope="row"> elements, reducing row-level semantic information. Again, expected for the user's explicit choice.
  • Existing accessibility attributes (role="region", tabIndex, aria-label) for scrollable tables are correctly preserved and unaffected.
  • No new interactive elements are introduced, so no new keyboard/focus concerns.

Recommendations

  1. Consider adding a typing test (lib/tests/streamlit/typing/table_types.py) to verify the hide_index and hide_header parameter types are correctly exposed in the public API. (Both reviewers noted this.)

  2. E2E test consolidation: Consider consolidating the 10 new E2E test functions into 2–3 aggregated scenario tests to reduce browser loads, per the E2E AGENTS.md guidance. (Style preference, not blocking.)

  3. Document the behavioral change: Since existing tables will look different after this change, consider noting this in release notes. Users who relied on seeing the RangeIndex or auto-generated headers will need to add hide_index=False or hide_header=False.

  4. Minor: The new docstring examples 4 and 5 could benefit from .. output:: directives for consistency with examples 1 and 2.

Verdict

APPROVED: Well-implemented feature with clean code, thorough test coverage across all layers, and correct protobuf wire compatibility. The critical concern raised by one reviewer about double-consumption of one-shot data sources was verified to be incorrect — the code specifically handles this via the converted_df parameter pattern. The auto-hide behavioral change is intentional and users can opt out. Minor improvements (typing tests, E2E consolidation, release notes) are recommended but not blocking.


Consolidated review by opus-4.6-thinking. Reviewed models: gpt-5.3-codex-high, opus-4.6-thinking (2/2 expected reviews received).


📋 Review by `gpt-5.3-codex-high`

Summary

This PR adds hide_index and hide_header to st.table, including backend auto-hide logic, frontend rendering support, protobuf fields, Python/TS unit tests, e2e tests, and snapshot updates.

I found one merge-blocking regression in the backend implementation for non-DataFrame inputs.

Code Quality

  • High: Non-DataFrame inputs are converted twice, and the first conversion can consume one-shot data sources before rendering.
    • lib/streamlit/elements/table.py:355 calls _compute_hide_index(...), which falls back to convert_anything_to_pandas_df(...) for non-DataFrame inputs at lib/streamlit/elements/table.py:137-139.
    • The same original data is then marshalled again at lib/streamlit/elements/table.py:373 via marshall_table(...).
    • For one-shot inputs (e.g. DB cursors, map, iterators/generators), convert_anything_to_pandas_df consumes data (lib/streamlit/dataframe_util.py:691-698, lib/streamlit/dataframe_util.py:746-748), so the second pass can render truncated or empty tables.

Test Coverage

Coverage is strong for the main happy paths:

  • Python unit tests validate explicit and auto behaviors for hide_index / hide_header.
  • Frontend unit tests validate header/index visibility toggles.
  • E2E tests plus snapshots cover key visual combinations.

Gap:

  • No regression test covers one-shot iterable/cursor inputs with default hide_index=None, which is where the double-consumption bug occurs.

Backwards Compatibility

The protobuf field additions are additive (Table.proto fields 3 and 4) and compatible. API expansion is keyword-only and non-breaking at signature level.

Behavioral default changes (auto-hiding some index/header cases) are intentional per PR scope.

Security & Risk

No direct security issues identified.

Primary risk is functional regression for data correctness on single-pass data sources (cursor/iterator-like inputs), which can silently drop displayed rows.

Accessibility

Frontend changes keep semantic table markup and do not introduce new interactive controls.

hide_header=True intentionally removes <thead>, which reduces screen-reader column context by design, but this is expected for an explicit hide option.

Recommendations

  1. Fix st.table to avoid re-consuming non-repeatable inputs when computing auto hide_index (single conversion path or reuse converted dataframe for marshalling).
  2. Add unit tests for one-shot inputs (at least DB cursor and iterator/map) to assert rendered data is unchanged with default hide_index=None.
  3. Add a targeted regression test for pyarrow.Table (or other direct-arrow path) to ensure auto-hide logic does not force unnecessary pandas conversion.

Verdict

CHANGES REQUESTED: The feature implementation is close, but the current backend logic can consume one-shot inputs before rendering and produce incorrect table output.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR adds two new parameters — hide_index and hide_header — to st.table(). Both accept bool | None:

  • hide_index: When None (default), auto-hides the index if it's a default RangeIndex; shows custom indices. Explicit True/False overrides.
  • hide_header: When None (default), auto-hides column headers for data formats without user-defined column names (dict, list, numpy, set, tuple, etc.). Explicit True/False overrides.

Changes span the full stack: protobuf schema (Table.proto), Python backend (table.py), React frontend (Table.tsx), and comprehensive tests (Python unit, frontend unit, and E2E with snapshots).

Code Quality

The implementation is clean and follows existing patterns well.

Backend (lib/streamlit/elements/table.py):

  • _compute_hide_index and _compute_hide_header are well-structured module-level functions with proper docstrings, type annotations, and from __future__ import annotations.
  • The _HIDE_HEADER_DATA_FORMATS set correctly identifies data formats without user-defined column names. The selection (KEY_VALUE_DICT, LIST_OF_ROWS, LIST_OF_VALUES, NUMPY_LIST, NUMPY_MATRIX, SET_OF_VALUES, TUPLE_OF_VALUES, PANDAS_ARRAY, PYARROW_ARRAY) is well-considered — formats with explicit column names (COLUMN_VALUE_MAPPING, LIST_OF_RECORDS, PANDAS_DATAFRAME, etc.) are correctly excluded.
  • Smart avoidance of double DataFrame conversion via the converted_df parameter — the pre-converted DataFrame from is_unevaluated_data_object handling is reused in _compute_hide_index.
  • Good handling of Styler objects by accessing data.data to inspect the underlying DataFrame's index.
  • The data_format is determined before the unevaluated data conversion, which is correct since determine_data_format inspects the original input type.

Frontend (frontend/lib/src/components/elements/Table/Table.tsx):

  • hideIndex and hideHeader are derived during render (not in effects), following React best practices per the AGENTS.md.
  • Index columns are filtered at render time by returning null from map() in both generateTableHeader and generateTableRow — the underlying Quiver data is untouched.
  • visibleNumColumns is correctly recalculated for the empty table cell's colSpan.
  • Sticky header/index logic correctly accounts for hideHeader and hideIndex (e.g., enableStickyHeaders is false when hideHeader is true).

Protobuf (proto/streamlit/proto/Table.proto):

  • Two new bool fields at field numbers 3 and 4 — clean additions, no conflicts.

Docstrings: The new parameter docstrings follow the NumPy style, use double backticks for inline code, and list options with bullet points — consistent with existing parameter documentation. The two new examples (4 and 5) are useful but lack .. output:: directives. This is minor since Example 3 also lacks one, so it's consistent within the file.

Test Coverage

Test coverage is thorough across all layers:

Python unit tests (lib/tests/streamlit/elements/table_test.py):

  • Explicit True/False for both parameters (parameterized).
  • Auto-hide logic for RangeIndex vs. custom index.
  • Auto-hide logic for DataFrames vs. simple data formats (dict, list, nested list, numpy).
  • Styler object support (with both RangeIndex and custom index).
  • Explicit override of auto-hide behavior.
  • Combined hide_index=True + hide_header=True.

Frontend unit tests (Table.test.tsx):

  • Hide/show index columns with hideIndex true/false.
  • Hide/show header row with hideHeader true/false.
  • Anti-regression checks (data cells still present when index/header hidden; thead still present when hideHeader is false).

E2E tests (e2e_playwright/st_table_test.py):

  • 10 scenario-specific tests with both behavioral assertions and visual snapshots.
  • Covers: auto-hide RangeIndex, custom index shown, explicit true/false for both params, auto-hide for dict/list data, combined params, and MultiIndex with hide_index.

Minor observations on E2E tests:

  • Per the E2E AGENTS.md guidance to "prefer a single aggregated scenario test over many micro-tests when they share the same setup and page state," the 10 separate test functions could be consolidated into 2–3 aggregated tests (e.g., one for hide_index scenarios, one for hide_header scenarios) to reduce browser loads. This is a style preference, though, and the current approach is clearer for debugging failures.
  • Tests rely on nth() index-based access (indices 37–46), which is fragile if tables are added/removed earlier in the app. This is consistent with the existing test style in this file, but wrapping with st.container(key=...) would be more resilient.

Missing but optional test coverage:

  • No typing test file (lib/tests/streamlit/typing/table_types.py) was added for the new parameters. This is pre-existing (there was none before), but would be valuable for catching typing regressions on the public API.
  • No test for hide_index=True on an empty DataFrame (0 rows).

Backwards Compatibility

This is a behavioral breaking change for existing users. With the default None values:

  1. Index auto-hide: Any st.table(df) where df has a default RangeIndex (the common case for most DataFrames) will now hide the index column. Previously, the numeric index (0, 1, 2, ...) was always visible.
  2. Header auto-hide: Any st.table(dict_or_list) will now hide column headers. Previously, auto-generated headers like "0" were always visible.

This is reflected in the updated E2E snapshots — many existing numbered snapshots (st_table-0 through st_table-13, st_table-27 through st_table-37) were updated because the visual appearance changed.

Users can opt out with hide_index=False and/or hide_header=False. The change is labeled change:feature and impact:users, indicating it's intentional.

Wire protocol compatibility is preserved:

  • Old frontends receiving new proto fields will ignore unknown fields (proto3 behavior) and continue showing everything.
  • New frontends receiving old protos will see false for both bools (proto3 default), correctly showing index and headers.

Security & Risk

No security concerns identified. The changes are purely display-related:

  • No user input is used in unsafe ways.
  • No new endpoints or authentication changes.
  • No filesystem or network operations added.

Regression risk is low-to-moderate:

  • The auto-hide logic could produce unexpected behavior for edge-case data types not covered in tests (e.g., Polars DataFrames, Modin DataFrames, Snowpark DataFrames passed to _compute_hide_index). These go through the final convert_anything_to_pandas_df fallback path in _compute_hide_index, which should work correctly since the conversion produces a DataFrame with a RangeIndex by default.

Accessibility

  • When hideHeader is true, the <thead> element is not rendered, which means screen readers lose the column header context for table cells. This is an inherent trade-off of hiding headers, and is the expected behavior when users explicitly choose to hide them.
  • When hideIndex is true, <th scope="row"> elements are removed, reducing row-level semantic information. Again, this is the expected outcome of the user's choice.
  • The existing accessibility attributes for scrollable tables (role="region", tabIndex, aria-label) are correctly preserved and unaffected by these changes.
  • No new interactive elements are introduced, so no new keyboard/focus concerns.

Recommendations

  1. Consider adding a typing test (lib/tests/streamlit/typing/table_types.py) to verify the hide_index and hide_header parameter types are correctly exposed in the public API. This catches type signature regressions via mypy.

  2. E2E test consolidation: Consider consolidating the 10 new E2E test functions into 2–3 aggregated scenario tests (one for hide_index scenarios, one for hide_header scenarios, one for combined). This follows the E2E AGENTS.md guidance to reduce browser loads while maintaining the same coverage.

  3. Document the behavioral change: Since existing tables will look different after this change (auto-hidden index/headers), consider noting this in release notes or a migration guide. Users who relied on seeing the RangeIndex or auto-generated headers will need to add hide_index=False or hide_header=False.

  4. Minor: The new docstring examples 4 and 5 could benefit from .. output:: directives with demo app URLs for consistency with examples 1 and 2.

Verdict

APPROVED: Well-implemented feature with clean code, thorough test coverage across all layers, and correct protobuf wire compatibility. The auto-hide behavioral change is intentional and users can opt out. Minor improvements (typing tests, E2E consolidation, release notes) are recommended but not blocking.


This is an automated AI review by opus-4.6-thinking.

@lukasmasuch lukasmasuch changed the title [feature] Add hide_index and hide_header to st.table [feature] Add hide_index and hide_header to st.table Mar 4, 2026
- Add typing test for hide_index and hide_header parameter types
- Consolidate 10 E2E tests into 2 aggregated scenario tests to reduce browser loads
- Add .. output:: directives to docstring examples 4 and 5 for consistency
Unevaluated data objects (Snowpark, PySpark, generators, DB cursors, etc.)
always produce a default RangeIndex when converted to pandas DataFrame.
Instead of passing a pre-converted DataFrame to _compute_hide_index,
we now directly assume hide_index=True for these data types.

This removes the data_df parameter from _compute_hide_index and
simplifies the control flow in st.table.
Copy link
Copy Markdown
Collaborator

@sfc-gh-lwilby sfc-gh-lwilby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lukasmasuch lukasmasuch merged commit 87df906 into develop Mar 10, 2026
44 checks passed
@lukasmasuch lukasmasuch deleted the feature/table-hide-index-hide-header branch March 10, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add hide_index to st.table st.dataframe / st.table new parameter: hide_header, hide_index for column headers

4 participants