OrgChart
Render an app-owned reporting tree with the same surface behavior as FamilyTree.
OrgChart renders a reporting hierarchy from app-owned nodes. For the easiest API, write the chart as a nested reporting tree with createOrgChart, then pass the result straight into OrgChart.
Props
type OrgChartProps<Person> = {
nodes: OrgChartNode<Person>[];
rootId?: string;
card?: React.ComponentType<OrgChartCardProps<Person>>;
className?: string;
style?: React.CSSProperties;
cardClassName?: string;
edgeClassName?: string;
interactionMode?: "pan" | "scroll" | "none";
selected?: string;
collapsed?: string[];
onPersonClick?: (person: Person, personId: string) => void;
};
type OrgChartNode<Person> = {
id: string;
person: Person;
parentId?: string | null;
order?: number;
};
type OrgChartBranch<Person> = {
id: string;
person: Person;
reports?: OrgChartBranch<Person>[];
order?: number;
};Example
Each reports array is the next generation in the chart.
import { OrgChart, createOrgChart } from "@memoir/tree";
import type { OrgChartCardProps } from "@memoir/tree";
import "@memoir/tree/styles.css";
type Person = {
name: string;
role: string;
};
const chart = createOrgChart({
id: "ceo",
person: { name: "Avery", role: "CEO" },
reports: [
{
id: "eng",
person: { name: "Morgan", role: "Engineering" },
reports: [{ id: "platform", person: { name: "Casey", role: "Platform" } }],
},
{ id: "design", person: { name: "Riley", role: "Design" } },
],
});
function PersonCard({
collapsed: _collapsed,
depth: _depth,
directReports,
focused: _focused,
generation,
managerId: _managerId,
person,
personId: _personId,
selected: _selected,
...props
}: OrgChartCardProps<Person>) {
return (
<article {...props}>
<strong>{person.name}</strong>
<small>
{person.role} - generation {generation} - {directReports.length} reports
</small>
</article>
);
}
export function Page() {
return <OrgChart {...chart} card={PersonCard} />;
}createOrgChart returns the exact props OrgChart needs, plus metadata that makes it easy to inspect the org:
chart.rootId;
// "ceo"
chart.nodes;
// [
// { id: "ceo", person: { name: "Avery", role: "CEO" }, parentId: null, order: 0 },
// { id: "eng", person: { name: "Morgan", role: "Engineering" }, parentId: "ceo", order: 0 },
// { id: "platform", person: { name: "Casey", role: "Platform" }, parentId: "eng", order: 0 },
// { id: "design", person: { name: "Riley", role: "Design" }, parentId: "ceo", order: 1 },
// ]
chart.generations;
// [
// { generation: 0, personIds: ["ceo"], count: 1 },
// { generation: 1, personIds: ["eng", "design"], count: 2 },
// { generation: 2, personIds: ["platform"], count: 1 },
// ]
chart.maxGeneration;
// 2
chart.reportLines;
// [
// { managerId: "ceo", reportId: "eng" },
// { managerId: "eng", reportId: "platform" },
// { managerId: "ceo", reportId: "design" },
// ]Return fields:
rootId: the top person in the chart.nodes: the flat{ id, person, parentId }list used byOrgChart.generations: each generation depth with the IDs in that row.maxGeneration: the deepest generation in the chart.reportLines: simple{ managerId, reportId }pairs for who reports to who.
Sibling order follows the reports array by default. Pass order on any branch when you want a custom order.
If your data already comes from a database as flat rows, you can still pass nodes directly.
Surface
The root surface is intentionally shared across tree types:
interactionMode="pan"clips overflow and supports drag-panning.interactionMode="scroll"exposes native scrollbars.interactionMode="none"clips without navigation.styleandclassNameapply to the surface, not each node.
Styling Hooks
OrgChart emits generic tree hooks and org-specific hooks:
data-tree-surfacedata-tree-canvasdata-tree-carddata-tree-card-positionerdata-tree-edgedata-org-chartdata-org-canvasdata-org-carddata-org-card-positionerdata-org-edge