Gathering detailed insights and metrics for @itwin/presentation-hierarchies-react
Gathering detailed insights and metrics for @itwin/presentation-hierarchies-react
Gathering detailed insights and metrics for @itwin/presentation-hierarchies-react
Gathering detailed insights and metrics for @itwin/presentation-hierarchies-react
Monorepo for iTwin.js Presentation Library
npm install @itwin/presentation-hierarchies-react
Typescript
Module System
Node Version
NPM Version
@itwin/presentation-hierarchies@1.6.0
Updated on Jul 15, 2025
@itwin/presentation-hierarchies-react@1.7.0
Updated on Jul 15, 2025
@itwin/presentation-hierarchies-react@1.6.9
Updated on Jul 04, 2025
@itwin/unified-selection@1.4.3-alpha.0
Updated on Jun 18, 2025
@itwin/presentation-hierarchies@2.0.0-alpha.1
Updated on Jun 18, 2025
@itwin/presentation-core-interop@1.4.0-alpha.2
Updated on Jun 18, 2025
TypeScript (98.5%)
JavaScript (1.15%)
SCSS (0.16%)
CSS (0.14%)
Shell (0.03%)
HTML (0.02%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
5 Stars
658 Commits
27 Watchers
22 Branches
30 Contributors
Updated on Jul 15, 2025
Latest Version
1.7.0
Package Id
@itwin/presentation-hierarchies-react@1.7.0
Unpacked Size
579.74 kB
Size
77.60 kB
File Count
127
NPM Version
10.9.2
Node Version
22.17.0
Published on
Jul 15, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
10
3
33
Copyright © Bentley Systems, Incorporated. All rights reserved. See LICENSE.md for license terms and full copyright notice.
The @itwin/presentation-hierarchies-react
package provides APIs for building a headless UI for rendering tree components based on data in an iTwin.js iModel. In addition, it delivers a set of iTwinUI-based components for rendering the tree.
The package provides different flavors of the same hook for creating and managing state of a tree component:
Feature \ Hook | useTree | useIModelTree | useUnifiedSelectionTree | useIModelUnifiedSelectionTree |
---|---|---|---|---|
Supported data source | any | iModel | any | iModel |
Integration with Unified Selection system | ❌ | ❌ | ✔️ | ✔️ |
All these hooks return the same state object, which contains properties and functions to manage the tree component:
isLoading
is a boolean indicating whether the root tree nodes are being loaded. Set to true
on initial load and on reload (e.g. when iModel data changes).
rootNodes
is an array of root tree nodes and is what the component should render. There are several types of nodes:
PresentationHierarchyNode
is the primary type of node, created based on the hierarchy definition. The isPresentationHierarchyNode
type guard utility may be used to check if a node is of this type.PresentationInfoNode
is a non-expandable, non-selectable informational type of node, generally created when for some reason we don't have any real nodes to show. There may be different reasons like filtered-out nodes, too large result set, a network error, etc. The type
attribute of the node indicates that.expandNode
function to expand or collapse a node.
isNodeSelected
and selectNodes
function to inspect and change tree selection.
getNode
function to get node by its id.
getHierarchyLevelDetails
function to access details of a specific hierarchy level. The returned object provides access to:
reloadTree
function to reload part of the tree, optionally keeping its state.
setFormatter
function to set active node label formatter.
useTree
propsThe hook takes a single required prop:
getHierarchyProvider
is a factory function that creates a hierarchy provider, returning the hierarchy the tree component will render. The @itwin/presentation-hierarchies
package describes the concept of hierarchy provider in more detail.useUnifiedSelectionTree
propsIn addition to props required by useTree
, the hook additionally requires:
selectionStorage
- unified selection storage used across different app's components, allowing them all to share selection state.sourceName
- a string that distinguishes selection changes being made by different components. The value should be unique for each component.useIModelTree
propsThe hook takes 2 required properties:
imodelAccess
provides access to iModel's data and metadata, required to build the hierarchy. Generally, @itwin/presentation-core-interop
and @itwin/presentation-shared
packages are used to create this object:
1import { IModelConnection } from "@itwin/core-frontend";
2import { SchemaContext } from "@itwin/ecschema-metadata";
3import { ECSchemaRpcLocater } from "@itwin/ecschema-rpcinterface-common";
4import { createCachingECClassHierarchyInspector } from "@itwin/presentation-shared";
5import { createECSchemaProvider, createECSqlQueryExecutor, createIModelKey } from "@itwin/presentation-core-interop";
6import { createLimitingECSqlQueryExecutor, createNodesQueryClauseFactory, HierarchyDefinition } from "@itwin/presentation-hierarchies";
7
8// Not really part of the package, but we need SchemaContext to create the tree state. It's
9// recommended to cache the schema context and reuse it across different application's components to
10// avoid loading and storing same schemas multiple times.
11const imodelSchemaContextsCache = new Map<string, SchemaContext>();
12
13function getIModelSchemaContext(imodel: IModelConnection) {
14 const imodelKey = createIModelKey(imodel);
15 let context = imodelSchemaContextsCache.get(imodelKey);
16 if (!context) {
17 context = new SchemaContext();
18 context.addLocater(new ECSchemaRpcLocater(imodel.getRpcProps()));
19 imodelSchemaContextsCache.set(imodelKey, context);
20 imodel.onClose.addListener(() => imodelSchemaContextsCache.delete(imodelKey));
21 }
22 return context;
23}
24
25function createIModelAccess(imodel: IModelConnection) {
26 const schemaProvider = createECSchemaProvider(getIModelSchemaContext(imodel));
27 return {
28 imodelKey: createIModelKey(imodel),
29 ...schemaProvider,
30 // while caching for hierarchy inspector is not mandatory, it's recommended to use it to improve performance
31 ...createCachingECClassHierarchyInspector({ schemaProvider, cacheSize: 100 }),
32 // the second argument is the maximum number of rows the executor will return - this allows us to
33 // avoid creating hierarchy levels of insane size (expensive to us and useless to users)
34 ...createLimitingECSqlQueryExecutor(createECSqlQueryExecutor(imodel), 1000),
35 };
36}
getHierarchyDefinition
is a factory function that creates a hierarchy definition, describing the hierarchy the tree component will render. The @itwin/presentation-hierarchies
package describes the concept of hierarchy definitions in more detail.
useIModelUnifiedSelectionTree
propsIn addition to props required by useIModelTree
, the hook additionally requires:
selectionStorage
- unified selection storage used across different app's components, allowing them all to share selection state.sourceName
- a string that distinguishes selection changes being made by different components. The value should be unique for each component.useSelectionHandler
hookThis is a React hook that helps implement different selection modes in a tree, whose state is managed through one of the tree state hooks.
It takes 3 required properties:
rootNodes
and selectNodes
are the corresponding properties from the tree state object, created using one of the tree state hooks.
selectionMode
is a string that defines the selection mode. It can be one of the following values:
none
- no selection is allowed,single
- only one node can be selected at a time,extended
- multiple nodes can be selected using shift and ctrl keys,multiple
- multiple nodes can be selected without using shift or ctrl keys.The returned object contains 2 functions, that should be called by the node renderer: onNodeClick
and onNodeKeyDown
.
Our tree renderer implementation calls this hook and passes the callbacks to the node renderer, so there's no need to use it unless implementing a custom tree renderer.
While the package provides a headless UI, it also delivers a set of iTwinUI-based components for rendering the tree, which should cover majority of use cases. Consumers using the below components are required to provide a compatible @itwin/itwinui-react
package, which is an optional peer dependency to this package.
TreeRenderer
The component is based on iTwinUI Tree component and uses our TreeNodeRenderer
to render the nodes. In addition, it makes use of the useSelectionHandler
hook to add selection modes' support.
The iTwinUI Tree component requires a getNode
function that maps nodes to NodeData<TNode>
objects. Our TreeRenderer
uses createRenderedTreeNodeData
function for this purpose, and it's available for consumers as well, in case a custom iTwinUI Tree component implementation is being written.
TreeNodeRenderer
The component is based on TreeNode
component from iTwinUI library and supports the following features:
useSelectionHandler
hook.1import { IModelConnection } from "@itwin/core-frontend";
2import { SchemaContext } from "@itwin/ecschema-metadata";
3import { ECSchemaRpcLocater } from "@itwin/ecschema-rpcinterface-common";
4import { createCachingECClassHierarchyInspector } from "@itwin/presentation-shared";
5import { createECSchemaProvider, createECSqlQueryExecutor, createIModelKey } from "@itwin/presentation-core-interop";
6import { createLimitingECSqlQueryExecutor, createNodesQueryClauseFactory, HierarchyDefinition } from "@itwin/presentation-hierarchies";
7
8import { createBisInstanceLabelSelectClauseFactory, Props } from "@itwin/presentation-shared";
9
10import { TreeRenderer, useIModelUnifiedSelectionTree } from "@itwin/presentation-hierarchies-react";
11import { createStorage, SelectionStorage } from "@itwin/unified-selection";
12import { useEffect, useState } from "react";
13
14// Not really part of the package, but we need SchemaContext to create the tree state. It's
15// recommended to cache the schema context and reuse it across different application's components to
16// avoid loading and storing same schemas multiple times.
17const imodelSchemaContextsCache = new Map<string, SchemaContext>();
18
19function getIModelSchemaContext(imodel: IModelConnection) {
20 const imodelKey = createIModelKey(imodel);
21 let context = imodelSchemaContextsCache.get(imodelKey);
22 if (!context) {
23 context = new SchemaContext();
24 context.addLocater(new ECSchemaRpcLocater(imodel.getRpcProps()));
25 imodelSchemaContextsCache.set(imodelKey, context);
26 imodel.onClose.addListener(() => imodelSchemaContextsCache.delete(imodelKey));
27 }
28 return context;
29}
30
31function createIModelAccess(imodel: IModelConnection) {
32 const schemaProvider = createECSchemaProvider(getIModelSchemaContext(imodel));
33 return {
34 imodelKey: createIModelKey(imodel),
35 ...schemaProvider,
36 // while caching for hierarchy inspector is not mandatory, it's recommended to use it to improve performance
37 ...createCachingECClassHierarchyInspector({ schemaProvider, cacheSize: 100 }),
38 // the second argument is the maximum number of rows the executor will return - this allows us to
39 // avoid creating hierarchy levels of insane size (expensive to us and useless to users)
40 ...createLimitingECSqlQueryExecutor(createECSqlQueryExecutor(imodel), 1000),
41 };
42}
43
44// Not part of the package - this should be created once and reused across different components of the application.
45const unifiedSelectionStorage = createStorage();
46
47/** Component providing the selection storage and access to iModel. Usually this is done in a top-level component. */
48function MyTreeComponent({ imodel }: { imodel: IModelConnection }) {
49 const [imodelAccess, setIModelAccess] = useState<IModelAccess>();
50 useEffect(() => {
51 setIModelAccess(createIModelAccess(imodel));
52 }, [imodel]);
53
54 if (!imodelAccess) {
55 return null;
56 }
57
58 return <MyTreeComponentInternal imodelAccess={imodelAccess} selectionStorage={unifiedSelectionStorage} />;
59}
60
61type IModelAccess = Props<typeof useIModelUnifiedSelectionTree>["imodelAccess"];
62
63// The hierarchy definition describes the hierarchy using ECSQL queries; here it just returns all `BisCore.PhysicalModel` instances
64function getHierarchyDefinition({ imodelAccess }: { imodelAccess: IModelAccess }): HierarchyDefinition {
65 // Create a factory for building labels SELECT query clauses according to BIS conventions
66 const labelsQueryFactory = createBisInstanceLabelSelectClauseFactory({ classHierarchyInspector: imodelAccess });
67 // Create a factory for building nodes SELECT query clauses in a format understood by the provider
68 const nodesQueryFactory = createNodesQueryClauseFactory({ imodelAccess, instanceLabelSelectClauseFactory: labelsQueryFactory });
69 return {
70 defineHierarchyLevel: async () => [
71 {
72 fullClassName: "BisCore.PhysicalModel",
73 query: {
74 ecsql: `
75 SELECT
76 ${await nodesQueryFactory.createSelectClause({
77 ecClassId: { selector: "this.ECClassId" },
78 ecInstanceId: { selector: "this.ECInstanceId" },
79 nodeLabel: {
80 selector: await labelsQueryFactory.createSelectClause({ classAlias: "this", className: "BisCore.PhysicalModel" }),
81 },
82 hasChildren: false,
83 })}
84 FROM BisCore.PhysicalModel this
85 `,
86 },
87 },
88 ],
89 };
90}
91
92/** Internal component that creates and renders tree state. */
93function MyTreeComponentInternal({ imodelAccess, selectionStorage }: { imodelAccess: IModelAccess; selectionStorage: SelectionStorage }) {
94 const { rootNodes, setFormatter, isLoading, ...state } = useIModelUnifiedSelectionTree({
95 // the unified selection storage used by all app components let them share selection state
96 selectionStorage,
97 // the source name is used to distinguish selection changes being made by different components
98 sourceName: "MyTreeComponent",
99 // iModel access is used to build the hierarchy
100 imodelAccess,
101 // supply the hierarchy definition
102 getHierarchyDefinition,
103 });
104 if (!rootNodes) {
105 return "Loading...";
106 }
107 return <TreeRenderer {...state} rootNodes={rootNodes} />;
108}
Localization can be enabled for TreeRenderer
component and tree state hooks by providing an object with localized strings that will be used instead of the default English ones.
Example:
1import { Props } from "@itwin/presentation-shared"; 2 3import { useIModelUnifiedSelectionTree } from "@itwin/presentation-hierarchies-react"; 4 5type IModelAccess = Props<typeof useIModelUnifiedSelectionTree>["imodelAccess"]; 6 7const localizedStrings = { 8 // strings for the `useIModelUnifiedSelectionTree` hook 9 unspecified: "Unspecified", 10 other: "Other", 11 12 // strings for `TreeRenderer` and `TreeNodeRenderer` 13 loading: "Loading...", 14 filterHierarchyLevel: "Apply hierarchy filter", 15 clearHierarchyLevelFilter: "Clear active filter", 16 noFilteredChildren: "No child nodes match current filter", 17 resultLimitExceeded: "There are more items than allowed limit of {{limit}}.", 18 resultLimitExceededWithFiltering: "Please provide <link>additional filtering</link> - there are more items than allowed limit of {{limit}}.", 19 increaseHierarchyLimit: "<link>Increase the hierarchy level size limit to {{limit}}.</link>", 20 increaseHierarchyLimitWithFiltering: "Or, <link>increase the hierarchy level size limit to {{limit}}.</link>", 21}; 22 23function MyTreeComponent({ imodelAccess }: { imodelAccess: IModelAccess }) { 24 const { rootNodes, expandNode } = useIModelUnifiedSelectionTree({ 25 sourceName: "MyTreeComponent", 26 imodelAccess, 27 localizedStrings, 28 getHierarchyDefinition, 29 }); 30 if (!rootNodes) { 31 return localizedStrings.loading; 32 } 33 return <TreeRenderer rootNodes={rootNodes} expandNode={expandNode} localizedStrings={localizedStrings} onFilterClick={() => {}} />; 34}
In case the TreeNodeRenderer
component is used within a custom tree renderer, the tree component should supply localized strings through LocalizationContextProvider
:
1import { Props } from "@itwin/presentation-shared"; 2 3import { ComponentPropsWithoutRef, useCallback } from "react"; 4import { Tree } from "@itwin/itwinui-react"; 5import { 6 createRenderedTreeNodeData, 7 LocalizationContextProvider, 8 RenderedTreeNode, 9 TreeNodeRenderer, 10 TreeRenderer, 11} from "@itwin/presentation-hierarchies-react"; 12 13type TreeProps = ComponentPropsWithoutRef<typeof Tree<RenderedTreeNode>>; 14type TreeRendererProps = Props<typeof TreeRenderer>; 15 16function MyTreeRenderer({ rootNodes }: TreeRendererProps) { 17 const nodeRenderer = useCallback<TreeProps["nodeRenderer"]>((nodeProps) => { 18 return <TreeNodeRenderer {...nodeProps} onFilterClick={() => {}} expandNode={() => {}} />; 19 }, []); 20 21 const getNode = useCallback<TreeProps["getNode"]>((node) => createRenderedTreeNodeData(node, () => false), []); 22 23 return ( 24 <LocalizationContextProvider localizedStrings={localizedStrings}> 25 <Tree<RenderedTreeNode> data={rootNodes} nodeRenderer={nodeRenderer} getNode={getNode} enableVirtualization={true} /> 26 </LocalizationContextProvider> 27 ); 28}
No vulnerabilities found.
No security vulnerabilities found.