Setting up a Table component for Unified Selection

Interaction between Table component and Unified Selection is synchronized one way - from Unified Selection to the Table. That means that whenever Unified Selection changes, the content for the Table is reloaded to represent what is selected.

Reference

The Reference section in Unified Selection page describes the core APIs used in Unified Selection workflows.

The @itwin/presentation-components package delivers usePresentationTableWithUnifiedSelection hook to make setting up Table components to work with Unified Selection easier. The hook uses the general usePresentationTable, but updates the UsePresentationTableProps.keys prop every time Unified Selection changes.

As stated in the documentation of usePresentationTableWithUnifiedSelection, it should be used within Unified Selection context, or, simply put, within UnifiedSelectionContextProvider.

Example

The below example shows how to create a very basic presentation rules driven Table component and hook it into Unified Selection. The latter part is achieved by using usePresentationTableWithUnifiedSelection to create the data for the component, as opposed to using the general usePresentationTable.

function MyTable(props: { imodel: IModelConnection; selectionStorage: SelectionStorage }) { // the library provides a variation of `usePresentationTable` that updates table content based // on unified selection const { columns, rows, isLoading } = usePresentationTableWithUnifiedSelection({ imodel: props.imodel, ruleset, pageSize: 10, columnMapper: mapTableColumns, rowMapper: mapTableRow, selectionStorage: props.selectionStorage, }); // don't render anything if the table is loading if (isLoading) { return null; } // if we're not loading and still don't have any columns or the columns list is empty - there's nothing // to build the table from, which means we probably have nothing selected if (!columns || columns.length === 0) { return <>Select something to see properties</>; } // render a simple HTML table return ( <table> <thead> <tr> {columns.map((col, i) => ( <td key={i}>{col.label}</td> ))} </tr> </thead> <tbody> {rows.map((row, ri) => ( <tr key={ri}> {columns.map((col, ci) => ( <td key={ci}> <Cell record={row[col.id]} /> </td> ))} </tr> ))} </tbody> </table> ); } // cell renderer that uses `PropertyValueRendererManager` to render property values function Cell(props: { record: PropertyRecord | undefined }) { return <>{props.record ? PropertyValueRendererManager.defaultManager.render(props.record) : null}</>; } // a function that maps presentation type of column definition to something that table renderer knows how to render const mapTableColumns = (columnDefinitions: TableColumnDefinition) => ({ id: columnDefinitions.name, label: columnDefinitions.label, }); // a function that maps presentation type of row definition to something that table renderer knows how to render function mapTableRow(rowDefinition: TableRowDefinition) { const rowValues: { [cellKey: string]: PropertyRecord } = {}; rowDefinition.cells.forEach((cell) => { rowValues[cell.key] = cell.record; }); return rowValues; }

The above component needs to be used within the Unified Selection context:

// Create a single unified selection storage to be shared between all application's components const selectionStorage = createStorage(); function App() { // pass selection storage to the component to hook into it return <MyTable imodel={imodel} selectionStorage={selectionStorage} />; }

Last Updated: 29 April, 2025