How to Integrate Third-Party DOM Libraries with React

Give an imperative library a container element through a ref, initialize it in an Effect after commit, and destroy it in the cleanup.

6 min read

Libraries for charts, maps, and editors manage their own DOM. React cannot render them declaratively, so you give the library a container element and drive it imperatively through a ref and an Effect. This article uses Chart.js as the example. The same approach works for any library with a constructor and a destroy method.

The integration pattern

The pattern has three parts. Point a ref at a container element, create the library instance in an Effect after React commits, and destroy it in the cleanup. It is the same container-ref idea used to measure DOM elements, except the library owns the node.

Create the container

Attach a ref to a canvas, div, or other element the library will manage.

Initialize in an Effect

Run the Effect after commit and pass the node to the library constructor.

Clean up

Return a function that calls the library's destroy or dispose method.

A Chart.js example

Install Chart.js, then create a component that owns a canvas. The Effect builds the chart and the cleanup destroys it.

App.jsxApp.jsx
import { useRef, useEffect } from "react";
import Chart from "chart.js/auto";
 
function BarChart({ labels, values }) {
  const canvasRef = useRef(null);
 
  useEffect(() => {
    const chart = new Chart(canvasRef.current, {
      type: "bar",
      data: { labels, datasets: [{ label: "Sales", data: values }] },
    });
    return () => chart.destroy();
  }, [labels, values]);

The Effect creates the chart after the canvas commits. Passing labels and values as dependencies recreates the chart whenever the data changes.

App.jsxApp.jsx
  return <canvas ref={canvasRef} />;
}

The component renders a canvas that Chart.js draws into. The canvas stays empty until the Effect runs, so the chart appears only after commit. When the component unmounts, the cleanup calls destroy, which removes the chart and frees its resources.

Why cleanup matters

Without destroy, the chart keeps running after unmount and can leak canvases or event listeners. Leaked resources keep running after the component is gone, which can slow the page and cause duplicate updates.

Strict Mode mounts, unmounts, and remounts once in development, so the create and destroy cycle runs twice. That double run is expected and verifies that your cleanup is correct.

Recreate only when the data changes

The dependency array controls when the Effect re-runs. Include every reactive value the Effect reads, such as props and state used to build the chart.

Omitting a dependency leaves the chart showing stale data, while an empty array builds the chart once and never updates it. The container ref itself never goes in the array, because React keeps the same ref object across renders. React runs the cleanup before the next setup, so the old chart is destroyed and replaced rather than duplicated.

What to learn next

The same ref and Effect pairing appears when you measure your own DOM. Continue with common React ref errors to catch the mistakes that break this pattern.

Rune AI

Rune AI

Key Insights

  • Point a ref at a container element for the library.
  • Create the library instance in an Effect after commit.
  • Destroy the instance in the Effect cleanup.
  • Recreate on dependency changes by listing props in the array.
  • Strict Mode runs the create and destroy cycle twice in development.
RunePowered by Rune AI

Frequently Asked Questions

Why initialize a library in an Effect instead of during render?

Render must stay pure, and the DOM node does not exist until after commit. An Effect runs after commit, when the node is ready.

Why destroy the library in cleanup?

Most imperative libraries hold onto canvases, listeners, or timers. Destroying them in cleanup frees those resources when the component unmounts or re-runs.

What should go in the dependency array?

Every reactive value the Effect reads, such as props used to build the chart. That recreates the library when the data changes.

Conclusion

Integrate a third-party DOM library by giving it a container ref, creating the instance in an Effect after commit, and destroying it in cleanup. List the data the Effect reads in the dependency array.