- AEAmy ElsnerFounder & CEO
- AJAsiya JavayantProduct Lead
- AFAnna FaliUX Designer
- BDBernardo DominicProduct Manager
- OLOnyama LimbaEngineering Lead
- ESElwin SharvillFrontend Engineer
- SSStephen ShawBackend Engineer
Usage#
import { OrganizationChart } from '@primereact/ui/organizationchart';OrganizationChart.Nodes calls its render function once per node and builds the connectors and nesting for you, no recursion in your code. Each node is identified by uKey.
<OrganizationChart.Root value={data}>
<OrganizationChart.Nodes>
{({ node }) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>Examples#
Basic#
A basic chart renders a collection of nodes from the value.
- Founder
- Product Lead
- UX/UI Designer
- Product Manager
- Engineering Lead
- Frontend Developer
- Backend Developer
'use client';
import { OrganizationChart, type OrganizationChartNodeRow } from '@primereact/ui/organizationchart';
const data = [
{
key: '0',
label: 'Founder',
children: [
{
key: '0-0',
label: 'Product Lead',
children: [
{ key: '0-0-0', label: 'UX/UI Designer' },
{ key: '0-0-1', label: 'Product Manager' }
]
},
{
key: '0-1',
label: 'Engineering Lead',
children: [
{ key: '0-1-0', label: 'Frontend Developer' },
{ key: '0-1-1', label: 'Backend Developer' }
]
}
]
}
];
export default function BasicDemo() {
return (
<div className="flex items-center justify-center">
<OrganizationChart.Root value={data}>
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Collapsible#
Add OrganizationChart.Toggle with OrganizationChart.ToggleIndicator and its match property of expanded or collapsed to let a node collapse its subtree. A node is collapsible when it has children.
- Founder
- Product Lead
- UX/UI Designer
- Product Manager
- Engineering Lead
- Frontend Developer
- Backend Developer
'use client';
import { Minus } from '@primeicons/react/minus';
import { Plus } from '@primeicons/react/plus';
import { OrganizationChart, type OrganizationChartNodeRow } from '@primereact/ui/organizationchart';
const data = [
{
key: '0',
label: 'Founder',
children: [
{
key: '0-0',
label: 'Product Lead',
children: [
{ key: '0-0-0', label: 'UX/UI Designer' },
{ key: '0-0-1', label: 'Product Manager' }
]
},
{
key: '0-1',
label: 'Engineering Lead',
children: [
{ key: '0-1-0', label: 'Frontend Developer' },
{ key: '0-1-1', label: 'Backend Developer' }
]
}
]
}
];
export default function CollapsibleDemo() {
return (
<div className="flex items-center justify-center">
<OrganizationChart.Root value={data}>
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
<OrganizationChart.Toggle>
<OrganizationChart.ToggleIndicator match="expanded">
<Minus />
</OrganizationChart.ToggleIndicator>
<OrganizationChart.ToggleIndicator match="collapsed">
<Plus />
</OrganizationChart.ToggleIndicator>
</OrganizationChart.Toggle>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Controlled#
Drive expansion programmatically with the collapsedKeys and onCollapsedChange properties. A key present with true is collapsed; absent keys stay expanded.
- Founder
- Product Lead
- UX/UI Designer
- Product Manager
- Engineering Lead
- Frontend Developer
- Backend Developer
'use client';
import { Minus } from '@primeicons/react/minus';
import { Plus } from '@primeicons/react/plus';
import type { OrganizationChartCollapsedKeys, OrganizationChartCollapsedChangeEvent } from '@primereact/ui/organizationchart';
import { Button } from '@primereact/ui/button';
import { OrganizationChart, type OrganizationChartNodeRow } from '@primereact/ui/organizationchart';
import * as React from 'react';
const data = [
{
key: '0',
label: 'Founder',
children: [
{
key: '0-0',
label: 'Product Lead',
children: [
{ key: '0-0-0', label: 'UX/UI Designer' },
{ key: '0-0-1', label: 'Product Manager' }
]
},
{
key: '0-1',
label: 'Engineering Lead',
children: [
{ key: '0-1-0', label: 'Frontend Developer' },
{ key: '0-1-1', label: 'Backend Developer' }
]
}
]
}
];
export default function ControlledDemo() {
const [collapsedKeys, setCollapsedKeys] = React.useState<OrganizationChartCollapsedKeys>({});
return (
<div className="flex flex-col items-center gap-4">
<div className="flex gap-2">
<Button size="small" severity="secondary" onClick={() => setCollapsedKeys({})}>
Expand all
</Button>
<Button size="small" severity="secondary" onClick={() => setCollapsedKeys({ '0': true })}>
Collapse all
</Button>
</div>
<OrganizationChart.Root
value={data}
collapsedKeys={collapsedKeys}
onCollapsedChange={(e: OrganizationChartCollapsedChangeEvent) => setCollapsedKeys(e.value)}
>
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
<OrganizationChart.Toggle>
<OrganizationChart.ToggleIndicator match="expanded">
<Minus />
</OrganizationChart.ToggleIndicator>
<OrganizationChart.ToggleIndicator match="collapsed">
<Plus />
</OrganizationChart.ToggleIndicator>
</OrganizationChart.Toggle>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Content#
Render anything inside OrganizationChart.Content; the node payload carries your own data fields.
- AWS Cloudus-east-1
- ComputeWorkloads & runtime
- EC2Virtual servers
- LambdaServerless functions
- StorageData persistence
- S3Object storage
- RDSManaged databases
- NetworkingEdge & access
- CloudFrontGlobal CDN
- IAMAccess policies
'use client';
import { Bolt } from '@primeicons/react/bolt';
import { Box } from '@primeicons/react/box';
import { Cloud } from '@primeicons/react/cloud';
import { Database } from '@primeicons/react/database';
import { Globe } from '@primeicons/react/globe';
import { Minus } from '@primeicons/react/minus';
import { Plus } from '@primeicons/react/plus';
import { Server } from '@primeicons/react/server';
import { Shield } from '@primeicons/react/shield';
import type { TreeNode } from '@primereact/ui/organizationchart';
import { OrganizationChart, type OrganizationChartNodeRow } from '@primereact/ui/organizationchart';
import * as React from 'react';
interface ServiceNode extends TreeNode {
label: string;
description: string;
icon: React.ComponentType<{ className?: string }>;
accent: string;
children?: ServiceNode[];
}
const data: ServiceNode[] = [
{
key: '0',
label: 'AWS Cloud',
description: 'us-east-1',
icon: Cloud,
accent: 'bg-orange-500/10 text-orange-500',
children: [
{
key: '0_0',
label: 'Compute',
description: 'Workloads & runtime',
icon: Server,
accent: 'bg-sky-500/10 text-sky-500',
children: [
{ key: '0_0_0', label: 'EC2', description: 'Virtual servers', icon: Server, accent: 'bg-sky-500/10 text-sky-500' },
{ key: '0_0_1', label: 'Lambda', description: 'Serverless functions', icon: Bolt, accent: 'bg-sky-500/10 text-sky-500' }
]
},
{
key: '0_1',
label: 'Storage',
description: 'Data persistence',
icon: Database,
accent: 'bg-emerald-500/10 text-emerald-500',
children: [
{ key: '0_1_0', label: 'S3', description: 'Object storage', icon: Box, accent: 'bg-emerald-500/10 text-emerald-500' },
{ key: '0_1_1', label: 'RDS', description: 'Managed databases', icon: Database, accent: 'bg-emerald-500/10 text-emerald-500' }
]
},
{
key: '0_2',
label: 'Networking',
description: 'Edge & access',
icon: Globe,
accent: 'bg-violet-500/10 text-violet-500',
children: [
{ key: '0_2_0', label: 'CloudFront', description: 'Global CDN', icon: Globe, accent: 'bg-violet-500/10 text-violet-500' },
{ key: '0_2_1', label: 'IAM', description: 'Access policies', icon: Shield, accent: 'bg-violet-500/10 text-violet-500' }
]
}
]
}
];
export default function ContentDemo() {
return (
<div className="flex items-center justify-center">
<OrganizationChart.Root value={data}>
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => {
const item = node as ServiceNode;
const Icon = item.icon;
return (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<div className="flex items-center gap-2">
<div className={`size-9 rounded-md flex items-center justify-center shrink-0 ${item.accent}`}>
<Icon className="size-4!" />
</div>
<div className="flex flex-col items-start gap-0.5">
<div className="text-sm font-semibold leading-none">{item.label}</div>
<div className="text-xs leading-none opacity-75">{item.description}</div>
</div>
</div>
<OrganizationChart.Toggle>
<OrganizationChart.ToggleIndicator match="expanded">
<Minus />
</OrganizationChart.ToggleIndicator>
<OrganizationChart.ToggleIndicator match="collapsed">
<Plus />
</OrganizationChart.ToggleIndicator>
</OrganizationChart.Toggle>
</OrganizationChart.Content>
</OrganizationChart.Node>
);
}}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Selection#
Single#
Set selectionMode to single to keep at most one node selected. Bind the selection with selectionKeys and onSelectionChange.
- Founder
- Product Lead
- UX/UI Designer
- Product Manager
- Engineering Lead
- Frontend Developer
- Backend Developer
'use client';
import type { OrganizationChartSelectionKeys, OrganizationChartSelectionChangeEvent } from '@primereact/ui/organizationchart';
import { OrganizationChart, type OrganizationChartNodeRow } from '@primereact/ui/organizationchart';
import * as React from 'react';
const data = [
{
key: '0',
label: 'Founder',
children: [
{
key: '0-0',
label: 'Product Lead',
children: [
{ key: '0-0-0', label: 'UX/UI Designer' },
{ key: '0-0-1', label: 'Product Manager' }
]
},
{
key: '0-1',
label: 'Engineering Lead',
children: [
{ key: '0-1-0', label: 'Frontend Developer' },
{ key: '0-1-1', label: 'Backend Developer' }
]
}
]
}
];
export default function SingleSelectionDemo() {
const [selectionKeys, setSelectionKeys] = React.useState<OrganizationChartSelectionKeys>({});
const selected = Object.keys(selectionKeys).filter((k) => selectionKeys[k]);
return (
<div className="flex flex-col items-center gap-4">
<span className="text-sm text-muted-color">
Selected: <span className="font-mono">{selected.length ? selected.join(', ') : '—'}</span>
</span>
<OrganizationChart.Root
value={data}
selectionMode="single"
selectionKeys={selectionKeys}
onSelectionChange={(e: OrganizationChartSelectionChangeEvent) => setSelectionKeys(e.value)}
>
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Multiple#
With multiple, each node toggles independently.
- Founder
- Product Lead
- UX/UI Designer
- Product Manager
- Engineering Lead
- Frontend Developer
- Backend Developer
'use client';
import type { OrganizationChartSelectionKeys, OrganizationChartSelectionChangeEvent } from '@primereact/ui/organizationchart';
import { OrganizationChart, type OrganizationChartNodeRow } from '@primereact/ui/organizationchart';
import * as React from 'react';
const data = [
{
key: '0',
label: 'Founder',
children: [
{
key: '0-0',
label: 'Product Lead',
children: [
{ key: '0-0-0', label: 'UX/UI Designer' },
{ key: '0-0-1', label: 'Product Manager' }
]
},
{
key: '0-1',
label: 'Engineering Lead',
children: [
{ key: '0-1-0', label: 'Frontend Developer' },
{ key: '0-1-1', label: 'Backend Developer' }
]
}
]
}
];
export default function MultipleSelectionDemo() {
const [selectionKeys, setSelectionKeys] = React.useState<OrganizationChartSelectionKeys>({});
const selected = Object.keys(selectionKeys).filter((k) => selectionKeys[k]);
return (
<div className="flex flex-col items-center gap-4">
<span className="text-sm text-muted-color">
Selected: <span className="font-mono">{selected.length ? selected.join(', ') : '—'}</span>
</span>
<OrganizationChart.Root
value={data}
selectionMode="multiple"
selectionKeys={selectionKeys}
onSelectionChange={(e: OrganizationChartSelectionChangeEvent) => setSelectionKeys(e.value)}
>
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Checkbox#
In checkbox mode, OrganizationChart.Selection exposes isSelected, isPartiallySelected and toggle. Selecting a node cascades to its descendants and reflects a partial state on its ancestors.
- Founder
- Product Lead
- UX/UI Designer
- Product Manager
- Engineering Lead
- Frontend Developer
- Backend Developer
'use client';
import { Check } from '@primeicons/react/check';
import { Minus } from '@primeicons/react/minus';
import { Checkbox, type CheckboxRootChangeEvent } from '@primereact/ui/checkbox';
import { OrganizationChart, type OrganizationChartNodeRow, type OrganizationChartSelectionInstance } from '@primereact/ui/organizationchart';
const data = [
{
key: '0',
label: 'Founder',
children: [
{
key: '0-0',
label: 'Product Lead',
children: [
{ key: '0-0-0', label: 'UX/UI Designer' },
{ key: '0-0-1', label: 'Product Manager' }
]
},
{
key: '0-1',
label: 'Engineering Lead',
children: [
{ key: '0-1-0', label: 'Frontend Developer' },
{ key: '0-1-1', label: 'Backend Developer' }
]
}
]
}
];
export default function CheckboxSelectionDemo() {
return (
<div className="flex items-center justify-center">
<OrganizationChart.Root value={data} selectionMode="checkbox">
<OrganizationChart.Nodes>
{({ node }: OrganizationChartNodeRow) => (
<OrganizationChart.Node uKey={node.key}>
<OrganizationChart.Content className="flex items-center gap-2">
<OrganizationChart.Selection>
{({ isSelected, isPartiallySelected, toggle }: OrganizationChartSelectionInstance) => (
<Checkbox.Root
checked={isSelected}
indeterminate={isPartiallySelected}
onCheckedChange={(e: CheckboxRootChangeEvent) => toggle(e.originalEvent)}
size="small"
>
<Checkbox.Box>
<Checkbox.Indicator match="checked">
<Check />
</Checkbox.Indicator>
<Checkbox.Indicator match="indeterminate">
<Minus />
</Checkbox.Indicator>
</Checkbox.Box>
</Checkbox.Root>
)}
</OrganizationChart.Selection>
<OrganizationChart.Label>{node.label}</OrganizationChart.Label>
</OrganizationChart.Content>
</OrganizationChart.Node>
)}
</OrganizationChart.Nodes>
</OrganizationChart.Root>
</div>
);
}
Related#
Sub-Components#
See the Primitive API for OrganizationChart.Root, OrganizationChart.Nodes, OrganizationChart.Node, OrganizationChart.Content, OrganizationChart.Label, OrganizationChart.Toggle, OrganizationChart.ToggleIndicator and OrganizationChart.Selection.
Hooks#
See the Headless API for useOrganizationChart.
Accessibility#
See OrganizationChart Primitive for WAI-ARIA compliance details and keyboard support.