npm package: @lamvuvu43/react-org-chart
Description: A simple React wrapper around [d3-org-chart](https://github.com/bumbeishvili/org-chart) with custom node rendering and configurable link styles.
Latest version: 1.0.2


# react-org-chart

A simple React wrapper around [d3-org-chart](https://github.com/bumbeishvili/org-chart) with custom node rendering and configurable link styles.

---

## 📦 Installation

```bash
npm install @lamvuvu43/react-org-chart
```

---

## 🚀 Usage

```tsx
import OrgChartComponent from "react-org-chart";

const data = [
    {
        "name": "Ian Devling",
        "imageUrl": "https://raw.githubusercontent.com/bumbeishvili/Assets/master/Projects/D3/Organization%20Chart/cto.jpg",
        "area": "Corporate",
        "profileUrl": "http://example.com/employee/profile",
        "office": "CTO office",
        "tags": "Ceo,tag1,manager,cto",
        "isLoggedUser": "false",
        "positionName": "Chief Executive Officer ",
        "id": "O-6066",
        "parentId": "",
        "size": ""
    },
    {
        "name": "Davolio Nancy",
        "imageUrl": "https://raw.githubusercontent.com/bumbeishvili/Assets/master/Projects/D3/Organization%20Chart/general.jpg",
        "area": "Corporate",
        "profileUrl": "http://example.com/employee/profile",
        "office": "CEO office",
        "tags": "Ceo,tag1, tag2",
        "isLoggedUser": "false",
        "positionName": "CTO ",
        "id": "O-6067",
        "parentId": "O-6066",
        "size": ""
    },
];

export function NodeChartItem(
  d: any,
  idSelected?: string | number
) {
  const color = "#FFFFFF";
  const imageDiffVert = 25 + 2;
  const isSelected = idSelected === d.data.id;

  return `
    <div style="
      width: ${d.width}px;
      height: ${d.height}px;
      padding-top: ${imageDiffVert - 2}px;
      padding-left: 1px;
      padding-right: 1px;
    ">
      <div style="
        font-family: 'Inter', sans-serif;
        background-color: ${color};
        margin-left: -1px;
        width: ${d.width - 2}px;
        height: ${d.height - imageDiffVert}px;
        border-radius: 10px;
        border: ${isSelected ? `2px solid #e0240bff` : `1px solid #2fcd80`};
        box-shadow: ${isSelected ? '0 0 10px #e0240bff' : 'none'};
      ">
        <div style="
          display: flex;
          justify-content: flex-end;
          margin-top: 5px;
          margin-right: 8px;
        ">
          #${d.data.id}
        </div>

        <div style="
          background-color: ${color};
          margin-top: ${-imageDiffVert - 20}px;
          margin-left: 15px;
          border-radius: 100px;
          width: 50px;
          height: 50px;
        "></div>

        <div style="
          margin-top: ${-imageDiffVert - 20}px;
        ">
          <img src="${d.data.imageUrl}"
            style="
              margin-left: 20px;
              border-radius: 100px;
              width: 40px;
              height: 40px;
              border: 2px solid #cb117133;
            "
          />
        </div>

        <div style="
          font-size: 15px;
          margin-left: 20px;
          margin-top: 10px;
          background: linear-gradient(
            90deg,
            rgba(194,161,12,1) 0%,
            rgba(9,121,24,0.25) 35%,
            rgba(255,0,249,0.93) 100%
          );
          background-clip: text;
          color: transparent;
        ">
          ${d.data.name}
        </div>

        <div style="
          color: #716E7B;
          margin-left: 20px;
          margin-top: 3px;
          font-size: 10px;
        ">
          ${d.data.positionName}
        </div>
      </div>
    </div>
  `;
}

export function ActionButtons(chartRef) {
  return (
    <div>
      <button
        className="border rounded-md p-2 m-2"
        onClick={() => chartRef.current.zoomIn()}
      >
        Zoom In
      </button>
      <button
        className="border rounded-md p-2 m-2"
        onClick={() => chartRef.current.zoomOut()}
      >
        Zoom Out
      </button>
    </div>
  );
}
export default function OrgChartPage() {
  const [data, setData] = useState(null);
  const selectNodeId = useRef(null);
  let addNodeChildFunc = null;

  function addNode() {
    const node = {
      "name": "Demo Added Node",
      "imageUrl": "https://raw.githubusercontent.com/bumbeishvili/Assets/master/Projects/D3/Organization%20Chart/cto.jpg",
      "area": "Corporate",
      "profileUrl": "http://example.com/employee/profile",
      "office": "CTO office",
      "tags": "Ceo,tag1,manager,cto",
      "isLoggedUser": "false",
      "positionName": "Chief Executive Officer ",
      "id": "O-" + Math.floor(Math.random() * 1000),
      "parentId": selectNodeId.current,
      "size": "",
    };
    addNodeChildFunc(node);
  }

  function onNodeClick(node) {
    selectNodeId.current = node.data.id;
  }

  function handleSetClick(func) {
    addNodeChildFunc = func;
  }

  useEffect(() => {
    d3.csv(
      'https://raw.githubusercontent.com/bumbeishvili/sample-data/main/org.csv'
    ).then((data) => {
      setData(data);
    });
  }, [true]);
  return (
    <div>
      <button style={{ border: "1px", borderRadius: "5px", backgroundColor: "lightblue" }} onClick={() => addNode()}>Add node</button>
      <br></br>
      <OrgChartComponent
        linkFuncAddNode={(funcAddNode) => addNodeChildFunc = funcAddNode}
        onNodeClick={onNodeClick}
        data={data}
        selectNodeId={selectNodeId.current}
        nodeItem={NodeChartItem}
        actionButtons={ActionButtons}
      />
    </div>
  );
}

```

---

## ⚙️ Props

| Prop | Type | Description |
| ---- | ---- | ------------ |
| `data` | `[{ [key: string]: any }]` | Org chart data in hierarchical format |
| `linkFuncAddNode` | `(node: any) => void` | Function to get internal addNode function |
| `onNodeClick` | `(node: any) => void` | Callback when a node is clicked |
| `nodeItem` | `(d: any, nodeIdSelected?: string \| number) => string` | Custom node content renderer |
| `nodeConfig` | `{ width?: number, height?: number }` | Node size config |
| `lineOptions` | `{ stroke?: string, strokeWidth?: number }` | Line (link) color and width |
| `actionButtons` | `(chartRef: any) => React.ReactNode` | Custom action buttons, default is built-in |

---

## 🗂️ Features

✅ Uses **D3 Org Chart** under the hood  
✅ Custom node rendering with `nodeItem`  
✅ Configurable line `stroke` and `strokeWidth`  
✅ Support for external add node function  
✅ Click handler for each node  
✅ Action buttons slot

---

## 📄 License

MIT