Skip to content

UI Customization

<TableView> is a compound component. It renders a default layout (toolbars, table, footer), but you can override any part by passing children that replace the defaults.

Without any children, <TableView> renders:

TableView
├── Toolbars
│ ├── ActionsToolbar (filter/sort/settings buttons)
│ └── SortsFiltersToolbar (active filter/sort chips)
├── Table
│ ├── HeaderRow (column headers with menus)
│ └── Body (data rows)
└── Footer (add row, delete selected -- when editable)

Add buttons to the actions toolbar alongside the built-in ones:

import {
TableView,
Toolbars,
ActionsToolbar,
MainActionsToolbarGroup,
MainActionsToolbarGroupItems,
} from "@blocknote/block-view/react";
function MyTable({ table }) {
return (
<TableView table={table} editable>
<Toolbars>
<ActionsToolbar>
<MainActionsToolbarGroup>
{/* Built-in buttons (filter, sort, settings) */}
<MainActionsToolbarGroupItems />
{/* Your custom buttons */}
<button onClick={() => console.log("Export clicked!")}>Export</button>
<button onClick={() => console.log("Import clicked!")}>Import</button>
</MainActionsToolbarGroup>
</ActionsToolbar>
</Toolbars>
</TableView>
);
}

<MainActionsToolbarGroupItems /> renders the default toolbar buttons. Place your custom buttons before or after it.

Add items to every column header’s dropdown menu:

import { TableView, Table, HeaderRow } from "@blocknote/block-view/react";
import { Menu } from "@base-ui/react";
function MyTable({ table }) {
return (
<TableView table={table} editable>
<Table>
<HeaderRow
menuAfter={
<Menu.Item
onClick={(e) => {
// Access the column from the event context
console.log("Custom menu item clicked");
}}
>
Copy column data
</Menu.Item>
}
/>
</Table>
</TableView>
);
}

The menuAfter prop appends menu items after the default column menu items (rename, sort, filter, hide, delete).

Replace or extend the footer content:

import {
TableView,
Footer,
AddDocumentFooterButton,
DeleteSelectedDocumentsFooterButton,
useTableViewContext,
} from "@blocknote/block-view/react";
function CustomFooterContent() {
const { table } = useTableViewContext();
const hasSelection = table.getIsSomeRowsSelected();
return (
<>
<AddDocumentFooterButton />
{hasSelection && <DeleteSelectedDocumentsFooterButton />}
<button
onClick={() => {
const selected = table.getSelectedRowsById();
console.log("Selected rows:", Object.keys(selected));
}}
>
Log selected
</button>
<span style={{ marginLeft: "auto", color: "#666" }}>{table.rows.length} rows</span>
</>
);
}
function MyTable({ table }) {
return (
<TableView table={table} editable>
<Footer>
<CustomFooterContent />
</Footer>
</TableView>
);
}

Use useTableViewContext() inside any child of <TableView> to access the table instance and editable state.

Full Example: Custom Table with All Overrides

Section titled “Full Example: Custom Table with All Overrides”
import {
TableView,
Toolbars,
ActionsToolbar,
MainActionsToolbarGroup,
MainActionsToolbarGroupItems,
Table,
HeaderRow,
Body,
Footer,
AddDocumentFooterButton,
DeleteSelectedDocumentsFooterButton,
useTableViewContext,
} from "@blocknote/block-view/react";
import { Menu } from "@base-ui/react";
function RowCount() {
const { table } = useTableViewContext();
return <span style={{ marginLeft: "auto", color: "#888" }}>{table.rows.length} rows</span>;
}
function MyCustomTable({ table }) {
return (
<TableView table={table} editable>
{/* Custom toolbar with extra buttons */}
<Toolbars>
<ActionsToolbar>
<MainActionsToolbarGroup>
<MainActionsToolbarGroupItems />
<button onClick={() => alert("Exported!")}>Export CSV</button>
</MainActionsToolbarGroup>
</ActionsToolbar>
</Toolbars>
{/* Table with custom column menu items */}
<Table>
<HeaderRow
menuAfter={<Menu.Item onClick={() => alert("Copied!")}>Copy column</Menu.Item>}
/>
<Body />
</Table>
{/* Custom footer */}
<Footer>
<AddDocumentFooterButton />
<DeleteSelectedDocumentsFooterButton />
<RowCount />
</Footer>
</TableView>
);
}

These are all exported from @blocknote/block-view/react:

  • TableView — Top-level wrapper. Props: table, editable, children
  • Toolbars — Container for toolbar rows
  • Table — Container for header and body
  • Footer — Container for footer content
  • ActionsToolbar — Primary toolbar row
  • SortsFiltersToolbar — Active filter/sort chip bar
  • MainActionsToolbarGroup — Group wrapper for toolbar buttons
  • MainActionsToolbarGroupItems — Renders the default toolbar buttons
  • ShowFiltersToolbarButton — Toggle filter bar
  • ShowSortsToolbarButton — Toggle sort bar
  • SortSettingsComboboxRoot — Sort settings popover
  • PropertyVisibilitySettingsComboboxRoot — Column visibility popover
  • TableSettingsMenuRoot — Settings dropdown menu
  • HeaderRow — Column headers. Props: menuAfter (extra menu items)
  • Body — Data rows
  • PropertyHeader — Individual column header
  • HeaderMenuRoot — Column header dropdown menu
  • HeaderResizeHandle — Column resize drag handle
  • AddPropertyHeader — ”+” button to add a new column
  • SelectAllRowsHeader — Checkbox header for row selection
  • AddDocumentFooterButton — ”+ New” button
  • DeleteSelectedDocumentsFooterButton — Delete selected rows button
  • useTableViewContext() — Returns { table, editable, ref }
  • useTableContext() — Returns table-specific context within <Table>
  • useToolbarsContext() — Returns toolbar-specific state

Toggle editing with the editable prop:

<TableView table={table} editable={false} />

When editable is false, cells are read-only, the footer is hidden, and toolbar actions that modify data are disabled. You can also control editability per-cell through useCollectionTableView:

const { table } = useCollectionTableView({
collectionManager: manager,
viewId,
propertyDefinitions: defaultPropertyDefinitions,
getEditable: (document, property) => {
// Make "name" column always editable, others read-only
return property.id === "name";
},
});