@memoir/tree

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 by OrgChart.
  • 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.
  • style and className apply to the surface, not each node.

Styling Hooks

OrgChart emits generic tree hooks and org-specific hooks:

  • data-tree-surface
  • data-tree-canvas
  • data-tree-card
  • data-tree-card-positioner
  • data-tree-edge
  • data-org-chart
  • data-org-canvas
  • data-org-card
  • data-org-card-positioner
  • data-org-edge

On this page