Skip to main content

Custom Render Hooks

React Complex Tree is completely unopinionated and allows you to customize every single node written to the DOM tree.

caution

React Complex Tree provides default renderers that fulfill the accessibility requirements for tree structures as specified by W3C. This may no longer hold if you implement your own renderers instead.

If you provide custom renderers, make sure to create a DOM structure that fulfills the accessibility requirements by W3C.

note

If you only want to make small adjustments to the styling of the tree like adaptions to spacing, fonts or colors, you can look into the styling guide to see how you can use custom CSS variables and rules to adapt how the tree rendered by the default render hooks looks like.

Minimalistic Example for custom render hooks

Live Editor
Result
Loading...
info

All currently available render hooks are documented in the TreeRenderProps interface.

Complex Example for custom render hooks

As part of the react-complex-tree monorepo, we maintain official render logic that generates a tree according to the UI framework BlueprintJS by Palantir. You can find the code for the custom render implementation here.

Customizing the render logic for tree items

The most interesting hook is probably the renderItem hook, which allows you to customize how individual tree items are rendered. When using this hook to render an item, you can use the provided TreeItemRenderContext to access the render details of the item (e.g. whether the user currently drags over this item, or whether it is selected), and directly alter the tree state (i.e. context.addtoselecteditems()).

The props.children prop contains the child nodes of the tree item. You need to render this so that children are displayed. In the above example, children are rendered as child nodes for the item node itself, according to W3C accessibility specifications. If you want to render a linear list of items, independent of item depth, for example because you want to implement a virtualized list, you can do so by rendering the children outside the item container:

renderItem={({ title, arrow, depth, context, children }) => {
const InteractiveComponent = context.isRenaming ? 'div' : 'button';
return (
<>
<li
{...context.itemContainerWithChildrenProps}
>
<InteractiveComponent
{...context.itemContainerWithoutChildrenProps}
{...context.interactiveElementProps}
>
{ arrow }
{ title }
</InteractiveComponent>
</li>
{children}
</>
);
}}

Make sure to provide the props-objects context.itemContainerWithoutChildrenProps and context.itemContainerWithChildrenProps to the respective elements in your DOM structure, the first to the node that contains the item and its children, and the second to the node that only contains the item without its children. This is necessary to compute sizing information during drags.

Furthermore, the context.interactiveElementProps props can be spread to the interactive element to implement default interaction handlers, so that clicking on an element invokes its primary actions, it is selected and focused etc. Those props implement the most common DOM interaction hooks and attach them to the tree state, meaning that you need to provide minimal implementation effort for custom renderers. You can omit those props if you want to implement custom interaction logic.

Note that, if you want to customize the way how mouse clicks interact with the tree state (i.e. whether clicking on a parent node should expand it or just focus it) should not be changed by providing custom DOM hooks, but by choosing a different interaction mode. Read more on interaction modes here.

caution

The InteractiveComponent = context.isRenaming ? 'div' : 'button' is important in case you want to support renaming items. If the tree item is always rendered as button, it's focusing behavior will cause a blur event when the rename starts, and revert the item back to the non-renaming state.