Tree View
A shadcn-style tree view component built with Ark UI primitives.
Tree
node_modules
zag-js
panda
@types
react
react-dom
src
app.tsx
index.ts
package.json
README.md
import {
createShowcaseTreeCollection,
TreeView,
TreeViewLabel,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
const TreeViewDemo = () => {
const collection = createShowcaseTreeCollection();
return (
<TreeView collection={collection}>
<TreeViewLabel>Tree</TreeViewLabel>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
indexPath={[index]}
key={node.id}
node={node}
/>
))}
</TreeViewTree>
</TreeView>
);
};
export default TreeViewDemo;
Installation
npx shadcn@latest add @ark-cn/tree-viewInstall the dependency required by this primitive:
npm install @ark-ui/react lucide-reactCopy the component source into your app:
TSXcomponents/ui/tree-view.tsx
"use client";
import {
createTreeCollection,
TreeView as TreeViewPrimitive,
} from "@ark-ui/react/tree-view";
import {
CheckIcon,
ChevronRight,
FileIcon,
GlobeIcon,
LayersIcon,
MinusIcon,
PlusIcon,
TrashIcon,
} from "lucide-react";
import { forwardRef } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
export type TreeViewProps<T> = TreeViewPrimitive.RootProps<T>;
export const TreeView = <T,>({ className, ...props }: TreeViewProps<T>) => (
<TreeViewPrimitive.Root
className={cn(
"flex w-full max-w-md flex-col gap-2 text-sm text-foreground",
className,
)}
data-slot="tree-view"
{...props}
/>
);
export type TreeViewLabelProps = TreeViewPrimitive.LabelProps;
export const TreeViewLabel = ({ className, ...props }: TreeViewLabelProps) => (
<TreeViewPrimitive.Label
className={cn("font-medium text-sm", className)}
data-slot="tree-view-label"
{...props}
/>
);
export type TreeViewTreeProps = TreeViewPrimitive.TreeProps;
export const TreeViewTree = forwardRef<HTMLDivElement, TreeViewTreeProps>(
({ className, ...props }, ref) => (
<TreeViewPrimitive.Tree
ref={ref}
className={cn("flex flex-col", className)}
data-slot="tree-view-tree"
{...props}
/>
),
);
export const TreeViewBranch = ({
className,
...props
}: TreeViewPrimitive.BranchProps) => (
<TreeViewPrimitive.Branch
className={cn("relative", className)}
data-slot="tree-view-branch"
{...props}
/>
);
export const TreeViewBranchControl = ({
className,
...props
}: TreeViewPrimitive.BranchControlProps) => (
<TreeViewPrimitive.BranchControl
className={cn(
"group flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-start transition-colors hover:bg-accent/60 data-selected:bg-accent data-disabled:pointer-events-none data-disabled:opacity-50",
className,
)}
data-slot="tree-view-branch-control"
{...props}
/>
);
export const TreeViewBranchIndicator = ({
className,
...props
}: TreeViewPrimitive.BranchIndicatorProps) => (
<TreeViewPrimitive.BranchIndicator
className={cn(
"inline-flex size-4 items-center justify-center text-muted-foreground transition-transform data-state-open:rotate-90",
className,
)}
data-slot="tree-view-branch-indicator"
{...props}
/>
);
export const TreeViewBranchText = ({
className,
...props
}: TreeViewPrimitive.BranchTextProps) => (
<TreeViewPrimitive.BranchText
className={cn(
"inline-flex min-w-0 flex-1 items-center gap-2 truncate",
className,
)}
data-slot="tree-view-branch-text"
{...props}
/>
);
export const TreeViewBranchContent = ({
className,
...props
}: TreeViewPrimitive.BranchContentProps) => (
<TreeViewPrimitive.BranchContent
className={cn("relative", className)}
data-slot="tree-view-branch-content"
{...props}
/>
);
export const TreeViewBranchIndentGuide = ({
className,
...props
}: TreeViewPrimitive.BranchIndentGuideProps) => (
<TreeViewPrimitive.BranchIndentGuide
className={cn("absolute inset-y-0 inset-s-4 w-px bg-border/70", className)}
data-slot="tree-view-branch-indent-guide"
{...props}
/>
);
export const TreeViewItem = ({
className,
...props
}: TreeViewPrimitive.ItemProps) => (
<TreeViewPrimitive.Item
className={cn(
"flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-start transition-colors hover:bg-accent/60 data-selected:bg-accent data-disabled:pointer-events-none data-disabled:opacity-50",
className,
)}
data-slot="tree-view-item"
{...props}
/>
);
export const TreeViewItemText = ({
className,
...props
}: TreeViewPrimitive.ItemTextProps) => (
<TreeViewPrimitive.ItemText
className={cn(
"inline-flex min-w-0 flex-1 items-center gap-2 truncate",
className,
)}
data-slot="tree-view-item-text"
{...props}
/>
);
export const TreeViewNodeCheckbox = ({
className,
...props
}: TreeViewPrimitive.NodeCheckboxProps) => (
<TreeViewPrimitive.NodeCheckbox
className={cn(
"inline-flex size-4 items-center justify-center rounded-sm border border-input bg-background text-primary data-state-checked:border-primary data-state-checked:bg-primary data-state-checked:text-primary-foreground data-state-indeterminate:border-primary data-state-indeterminate:bg-primary data-state-indeterminate:text-primary-foreground",
className,
)}
data-slot="tree-view-node-checkbox"
{...props}
/>
);
export const TreeViewNodeCheckboxIndicator = ({
className,
...props
}: TreeViewPrimitive.NodeCheckboxIndicatorProps) => (
<TreeViewPrimitive.NodeCheckboxIndicator
className={cn("inline-flex items-center justify-center", className)}
data-slot="tree-view-node-checkbox-indicator"
{...props}
/>
);
export const TreeViewNodeRenameInput = ({
className,
...props
}: TreeViewPrimitive.NodeRenameInputProps) => (
<TreeViewPrimitive.NodeRenameInput
className={cn(
"h-7 min-w-0 flex-1 rounded border border-primary/50 bg-background px-1.5 text-sm outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
className,
)}
data-slot="tree-view-node-rename-input"
{...props}
/>
);
export const TreeViewNodeProvider = TreeViewPrimitive.NodeProvider;
export const TreeViewNodeContext = TreeViewPrimitive.NodeContext;
export const TreeViewRootProvider = TreeViewPrimitive.RootProvider;
export type ShowcaseTreeNode = {
id: string;
name: string;
href?: string;
children?: ShowcaseTreeNode[];
};
export const TREE_VIEW_SHOWCASE_ROOT: ShowcaseTreeNode = {
id: "ROOT",
name: "",
children: [
{
id: "node_modules",
name: "node_modules",
children: [
{ id: "node_modules/zag-js", name: "zag-js" },
{ id: "node_modules/panda", name: "panda" },
{
id: "node_modules/@types",
name: "@types",
children: [
{ id: "node_modules/@types/react", name: "react" },
{ id: "node_modules/@types/react-dom", name: "react-dom" },
],
},
],
},
{
id: "src",
name: "src",
children: [
{ id: "src/app.tsx", name: "app.tsx", href: "/docs/introduction" },
{ id: "src/index.ts", name: "index.ts", href: "/docs/installation" },
],
},
{ id: "package.json", name: "package.json" },
{
id: "README.md",
name: "README.md",
href: "https://ark-ui.com/docs/components/tree-view",
},
],
};
export const createShowcaseTreeCollection = () =>
createTreeCollection<ShowcaseTreeNode>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: structuredClone(TREE_VIEW_SHOWCASE_ROOT),
});
export const TreeViewShowcaseNodeCheckboxControl = () => (
<TreeViewNodeCheckbox
className={cn(
"cursor-pointer peer relative flex size-4 shrink-0 items-center justify-center rounded border transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-invalid:border-destructive data-invalid:ring-3 data-invalid:ring-destructive/20 data-invalid:data-[state='checked']:border-primary dark:bg-input/30 dark:data-invalid:border-destructive/50 dark:data-invalid:ring-destructive/40 data-[state='checked']:border-primary data-[state='checked']:bg-primary data-[state='checked']:text-primary-foreground dark:data-[state='checked']:bg-primary",
)}
>
<TreeViewNodeCheckboxIndicator
className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
indeterminate={<MinusIcon />}
>
<CheckIcon />
</TreeViewNodeCheckboxIndicator>
</TreeViewNodeCheckbox>
);
type TreeViewShowcaseNodeActionsProps = {
node: ShowcaseTreeNode;
indexPath: number[];
onAdd?: (node: ShowcaseTreeNode, indexPath: number[]) => void;
onRemove?: (indexPath: number[]) => void;
};
export const TreeViewShowcaseNodeActions = ({
node,
indexPath,
onAdd,
onRemove,
}: TreeViewShowcaseNodeActionsProps) => {
if (!onAdd && !onRemove) return null;
return (
<div className="ms-auto inline-flex items-center gap-1">
{onRemove ? (
<Button
className="h-6 px-2 text-xs"
onClick={(event) => {
event.stopPropagation();
onRemove(indexPath);
}}
variant="outline"
size="icon-sm"
>
<TrashIcon className="size-3.5 text-muted-foreground" />
</Button>
) : null}
{onAdd && node.children ? (
<Button
className="h-6 px-2 text-xs"
onClick={(event) => {
event.stopPropagation();
onAdd(node, indexPath);
}}
variant="outline"
size="icon-sm"
>
<PlusIcon className="size-3.5 text-muted-foreground" />
</Button>
) : null}
</div>
);
};
export type TreeViewShowcaseNodeRendererProps = {
node: ShowcaseTreeNode;
indexPath: number[];
checkbox?: boolean;
links?: boolean;
rename?: boolean;
onAdd?: (node: ShowcaseTreeNode, indexPath: number[]) => void;
onRemove?: (indexPath: number[]) => void;
};
export const TreeViewShowcaseNodeRenderer = ({
node,
indexPath,
checkbox,
links,
rename,
onAdd,
onRemove,
}: TreeViewShowcaseNodeRendererProps) => (
<TreeViewNodeProvider indexPath={indexPath} node={node}>
<TreeViewNodeContext>
{(nodeState) =>
node.children ? (
<TreeViewBranch>
<TreeViewBranchControl
data-depth={nodeState.depth}
style={{
paddingInlineStart: "calc((var(--depth) - 1) * 22px)",
}}
>
<TreeViewBranchIndicator className="group/indicator">
<ChevronRight className="size-4 transition-transform rtl:rotate-180 group-data-[state=open]/indicator:rotate-90 rtl:group-data-[state=open]/indicator:rotate-90" />
</TreeViewBranchIndicator>
{checkbox ? <TreeViewShowcaseNodeCheckboxControl /> : null}
{rename && nodeState.renaming ? (
<TreeViewNodeRenameInput />
) : (
<TreeViewBranchText>
<LayersIcon className="size-3.5 text-muted-foreground" />
{node.name}
</TreeViewBranchText>
)}
<TreeViewShowcaseNodeActions
indexPath={indexPath}
node={node}
onAdd={onAdd}
onRemove={onRemove}
/>
</TreeViewBranchControl>
<TreeViewBranchContent>
<TreeViewBranchIndentGuide />
{node.children.map((child, childIndex) => (
<TreeViewShowcaseNodeRenderer
checkbox={checkbox}
indexPath={[...indexPath, childIndex]}
key={child.id}
links={links}
node={child}
onAdd={onAdd}
onRemove={onRemove}
rename={rename}
/>
))}
</TreeViewBranchContent>
</TreeViewBranch>
) : (
<TreeViewItem
asChild={Boolean(links && node.href)}
data-depth={nodeState.depth}
style={{
paddingInlineStart: "calc(((var(--depth) - 1) * 22px) + 22px)",
}}
>
{links && node.href ? (
<a
className="flex w-full items-center gap-2"
href={node.href}
rel={node.href.startsWith("http") ? "noreferrer" : undefined}
target={node.href.startsWith("http") ? "_blank" : undefined}
>
{checkbox ? <TreeViewShowcaseNodeCheckboxControl /> : null}
{rename && nodeState.renaming ? (
<TreeViewNodeRenameInput />
) : (
<TreeViewItemText>
<FileIcon className="size-3.5 text-muted-foreground" />
{node.name}
</TreeViewItemText>
)}
{node.href.startsWith("http") ? (
<GlobeIcon className="ms-auto size-3 text-muted-foreground" />
) : null}
<TreeViewShowcaseNodeActions
indexPath={indexPath}
node={node}
onAdd={onAdd}
onRemove={onRemove}
/>
</a>
) : (
<>
{checkbox ? <TreeViewShowcaseNodeCheckboxControl /> : null}
{rename && nodeState.renaming ? (
<TreeViewNodeRenameInput />
) : (
<TreeViewItemText>
<FileIcon className="size-3.5 text-muted-foreground" />
{node.name}
</TreeViewItemText>
)}
<TreeViewShowcaseNodeActions
indexPath={indexPath}
node={node}
onAdd={onAdd}
onRemove={onRemove}
/>
</>
)}
</TreeViewItem>
)
}
</TreeViewNodeContext>
</TreeViewNodeProvider>
);
export {
createTreeCollection,
useTreeView,
useTreeViewContext,
} from "@ark-ui/react/tree-view";
Update import aliases to match your project setup.
Usage
import * as TreeView from "@/components/ui/tree-view"Read exported parts in src/components/ui/tree-view.tsx and compose the primitive according to the Ark UI pattern for this component.
Examples
Basic
Tree
node_modules
zag-js
panda
@types
react
react-dom
src
app.tsx
index.ts
package.json
README.md
import {
createShowcaseTreeCollection,
TreeView,
TreeViewLabel,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
const TreeViewDemo = () => {
const collection = createShowcaseTreeCollection();
return (
<TreeView collection={collection}>
<TreeViewLabel>Tree</TreeViewLabel>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
indexPath={[index]}
key={node.id}
node={node}
/>
))}
</TreeViewTree>
</TreeView>
);
};
export default TreeViewDemo;
Checkbox
Checkbox Tree
node_modules
zag-js
panda
@types
react
react-dom
src
app.tsx
index.ts
package.json
README.md
import {
createShowcaseTreeCollection,
TreeView,
TreeViewLabel,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
const TreeViewCheckboxDemo = () => {
const collection = createShowcaseTreeCollection();
return (
<TreeView collection={collection} defaultCheckedValue={[]}>
<TreeViewLabel>Checkbox Tree</TreeViewLabel>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
checkbox
indexPath={[index]}
key={node.id}
node={node}
/>
))}
</TreeViewTree>
</TreeView>
);
};
export default TreeViewCheckboxDemo;
Mutation
Mutation Tree
node_modules
zag-js
panda
@types
react
react-dom
src
app.tsx
index.ts
package.json
README.md
import { useState } from "react";
import {
createShowcaseTreeCollection,
type ShowcaseTreeNode,
TreeView,
TreeViewLabel,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
const TreeViewMutationDemo = () => {
const [collection, setCollection] = useState(createShowcaseTreeCollection);
const handleRemove = (indexPath: number[]) => {
setCollection((previous) => previous.remove([indexPath]));
};
const handleAdd = (node: ShowcaseTreeNode, indexPath: number[]) => {
if (!node.children) return;
const nextNode: ShowcaseTreeNode = {
id: `${node.id}/new-${Date.now()}`,
name: `untitled-${node.children.length + 1}.tsx`,
};
setCollection((previous) => {
const currentNode = previous.at(indexPath);
if (!currentNode || !currentNode.children) return previous;
return previous.replace(indexPath, {
...currentNode,
children: [nextNode, ...currentNode.children],
});
});
};
return (
<TreeView collection={collection}>
<TreeViewLabel>Mutation Tree</TreeViewLabel>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
indexPath={[index]}
key={node.id}
node={node}
onAdd={handleAdd}
onRemove={handleRemove}
/>
))}
</TreeViewTree>
</TreeView>
);
};
export default TreeViewMutationDemo;
Rename
Rename Node (Press F2)
node_modules
zag-js
panda
@types
react
react-dom
src
app.tsx
index.ts
package.json
README.md
import { useState } from "react";
import {
createShowcaseTreeCollection,
TreeView,
TreeViewLabel,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
const TreeViewRenameDemo = () => {
const [collection, setCollection] = useState(createShowcaseTreeCollection);
return (
<TreeView
canRename={() => true}
collection={collection}
onRenameComplete={(details) => {
setCollection((previous) => {
const node = previous.at(details.indexPath);
if (!node) return previous;
return previous.replace(details.indexPath, {
...node,
name: details.label,
});
});
}}
>
<TreeViewLabel>Rename Node (Press F2)</TreeViewLabel>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
indexPath={[index]}
key={node.id}
node={node}
rename
/>
))}
</TreeViewTree>
</TreeView>
);
};
export default TreeViewRenameDemo;
Filtering
node_modules
zag-js
panda
@types
react
react-dom
src
app.tsx
index.ts
package.json
README.md
import { useFilter } from "@ark-ui/react/locale";
import { useState } from "react";
import { Input } from "@/components/ui/input";
import {
createShowcaseTreeCollection,
TreeView,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
const TreeViewFilteringDemo = () => {
const { contains } = useFilter({ sensitivity: "base" });
const [collection, setCollection] = useState(createShowcaseTreeCollection);
return (
<div className="flex flex-col gap-2">
<Input
className="h-8"
onChange={(event) => {
const value = event.target.value;
const initialCollection = createShowcaseTreeCollection();
const nextCollection =
value.length > 0
? initialCollection.filter((node) => contains(node.name, value))
: initialCollection;
setCollection(nextCollection);
}}
placeholder="Filter nodes..."
/>
<TreeView collection={collection}>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
indexPath={[index]}
key={node.id}
node={node}
/>
))}
</TreeViewTree>
</TreeView>
</div>
);
};
export default TreeViewFilteringDemo;
Links
import {
createTreeCollection,
TreeView,
TreeViewLabel,
TreeViewShowcaseNodeRenderer,
TreeViewTree,
} from "@/components/ui/tree-view";
type TreeNodeData = {
id: string;
name: string;
href?: string;
children?: TreeNodeData[];
};
const collection = createTreeCollection<TreeNodeData>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: "ROOT",
name: "",
children: [
{
id: "docs",
name: "Documentation",
children: [
{
id: "docs/getting-started",
name: "Getting Started",
href: "https://ark-ui.com/docs/getting-started",
},
{
id: "docs/installation",
name: "Installation",
href: "https://ark-ui.com/docs/installation",
},
{
id: "docs/components",
name: "Components",
children: [
{
id: "docs/components/accordion",
name: "Accordion",
href: "https://ark-ui.com/docs/components/accordion",
},
{
id: "docs/components/dialog",
name: "Dialog",
href: "https://ark-ui.com/docs/components/dialog",
},
{
id: "docs/components/menu",
name: "Menu",
href: "https://ark-ui.com/docs/components/menu",
},
],
},
],
},
{
id: "examples",
name: "Examples",
children: [
{
id: "examples/react",
name: "React Examples",
href: "https://ark-ui.com/examples/react",
},
{
id: "examples/vue",
name: "Vue Examples",
href: "https://ark-ui.com/examples/vue",
},
{
id: "examples/solid",
name: "Solid Examples",
href: "https://ark-ui.com/examples/solid",
},
],
},
{
id: "external",
name: "External Links",
children: [
{
id: "external/github",
name: "GitHub Repository",
href: "https://github.com/chakra-ui/zag",
},
{
id: "external/npm",
name: "NPM Package",
href: "https://www.npmjs.com/package/@zag-js/core",
},
{
id: "external/docs",
name: "Official Docs",
href: "https://zagjs.com",
},
],
},
{ id: "readme.md", name: "README.md", href: "/readme" },
{ id: "license", name: "LICENSE", href: "/license" },
],
},
});
const TreeViewLinksDemo = () => {
return (
<TreeView collection={collection}>
<TreeViewLabel>Docs</TreeViewLabel>
<TreeViewTree>
{collection.rootNode.children?.map((node, index) => (
<TreeViewShowcaseNodeRenderer
indexPath={[index]}
key={node.id}
links
node={node}
/>
))}
</TreeViewTree>
</TreeView>
);
};
export default TreeViewLinksDemo;
Virtualized
node_modules
src
package.json
README.md
Showing 4 visible nodes with virtual windowing.
import { ChevronDownIcon, FileIcon, LayersIcon } from "lucide-react";
import { useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import {
createShowcaseTreeCollection,
TreeViewBranchControl,
TreeViewBranchIndicator,
TreeViewBranchText,
TreeViewItem,
TreeViewItemText,
TreeViewNodeProvider,
TreeViewRootProvider,
TreeViewTree,
useTreeView,
} from "@/components/ui/tree-view";
const TREE_ROW_HEIGHT = 34;
const TREE_VIEWPORT_HEIGHT = 200;
const TreeViewVirtualizedDemo = () => {
const scrollAreaRef = useRef<HTMLDivElement | null>(null);
const [scrollTop, setScrollTop] = useState(0);
const tree = useTreeView({
collection: createShowcaseTreeCollection(),
scrollToIndexFn: ({ index }) => {
if (!scrollAreaRef.current) return;
scrollAreaRef.current.scrollTop = index * TREE_ROW_HEIGHT;
},
});
const visibleNodes = tree.getVisibleNodes();
const startIndex = Math.max(0, Math.floor(scrollTop / TREE_ROW_HEIGHT));
const endIndex = Math.min(
visibleNodes.length,
startIndex + Math.ceil(TREE_VIEWPORT_HEIGHT / TREE_ROW_HEIGHT) + 8,
);
const virtualRows = visibleNodes.slice(startIndex, endIndex);
const paddingTop = startIndex * TREE_ROW_HEIGHT;
const paddingBottom = Math.max(
0,
(visibleNodes.length - endIndex) * TREE_ROW_HEIGHT,
);
return (
<TreeViewRootProvider value={tree}>
<div className="mb-2 flex items-center gap-2">
<Button
className="h-8"
onClick={() => tree.collapse()}
variant="outline"
>
Collapse all
</Button>
<Button className="h-8" onClick={() => tree.expand()} variant="outline">
Expand all
</Button>
</div>
<TreeViewTree
className="max-w-none overflow-auto rounded-md border border-border/70"
onScroll={(event) => {
setScrollTop(event.currentTarget.scrollTop);
}}
ref={scrollAreaRef}
style={{ height: TREE_VIEWPORT_HEIGHT }}
>
<div style={{ paddingBottom, paddingTop }}>
{virtualRows.map(({ node, indexPath }) => {
const nodeState = tree.getNodeState({ indexPath, node });
return (
<TreeViewNodeProvider
indexPath={indexPath}
key={node.id}
node={node}
>
{nodeState.isBranch ? (
<TreeViewBranchControl
style={{ paddingInlineStart: nodeState.depth * 18 + 8 }}
>
<TreeViewBranchIndicator>
<ChevronDownIcon className="size-3" />
</TreeViewBranchIndicator>
<TreeViewBranchText>
<LayersIcon className="size-3.5 text-muted-foreground" />
{node.name}
</TreeViewBranchText>
</TreeViewBranchControl>
) : (
<TreeViewItem
style={{ paddingInlineStart: nodeState.depth * 18 + 8 }}
>
<TreeViewItemText>
<FileIcon className="size-3.5 text-muted-foreground" />
{node.name}
</TreeViewItemText>
</TreeViewItem>
)}
</TreeViewNodeProvider>
);
})}
</div>
</TreeViewTree>
<p className="mt-2 text-muted-foreground text-xs">
Showing {visibleNodes.length} visible nodes with virtual windowing.
</p>
</TreeViewRootProvider>
);
};
export default TreeViewVirtualizedDemo;
API reference
This component mirrors the upstream Ark UI primitive.
See the ARK UI documentation for the full API.
Accessibility
See the Ark UI documentation for clarification.