Custom Rendering
renderNode replaces row markup entirely, while TreeKit keeps handling state, keyboard nav, and ARIA on the row wrapper.
src
components
Button.tsx2.1 KB
Input.tsx1.4 KB
Modal.tsx3.8 KB
hooks
index.ts0.4 KB
public
package.json1.1 KB
README.md4.6 KB
Show code
<TreeKit
data={fileTree}
renderNode={(ctx) => (
<div
onClick={() => { ctx.selectNode(); if (ctx.hasChildren) ctx.toggleExpanded(); }}
style={{ paddingLeft: ctx.level * 16 }}
>
{ctx.hasChildren ? <FolderIcon open={ctx.expanded} /> : <FileIcon />}
<span>{ctx.node.label}</span>
{ctx.node.data?.sizeKb && <span>{ctx.node.data.sizeKb} KB</span>}
</div>
)}
/>renderNode receives a single context object per row with everything you need to render it and wire up interaction — no separate hooks, no prop drilling:
| Prop | Type | Default | Description |
|---|---|---|---|
| node | TreeNode<T> | — | The node itself, including your data field. |
| level | number | — | Zero-based depth, for indentation. |
| posInSet / setSize | number | — | 1-based position and sibling count — useful if you build your own aria-posinset. |
| expanded / checked / selected / disabled | boolean | CheckedState | — | Current state for this row. |
| hasChildren | boolean | — | Whether to render an expand affordance at all. |
| isLoading / loadError | boolean / unknown | — | Async-loading state — see Async Loading. |
| isMatch | boolean | — | Whether this node matched the active search term. |
| toggleExpanded / toggleChecked / selectNode / retryLoad | () => void | — | Bound actions — call directly from your own event handlers. |
The row wrapper still owns ARIA and focus
TreeKit renders your
renderNode output inside the row element that carries role="treeitem", aria-level, tabindex, etc. Don't render your own competing role="treeitem" inside it, and avoid nesting focusable elements other than the checkbox — see Accessibility for why the chevron itself is intentionally not a separate tab stop.