Using amCharts 5 with Remix 3

amCharts 5 works with Remix 3 through Remix's own tools: there is no separate package. For Remix 2, now React Router, see React Router.

A chart from a JSON config

The simplest way: the library's <am5-chart> element. It needs no client code. Load its script files in your document's <head>, with the files for the chart types the config uses:

<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/plugins/json.js"></script>
<script src="https://cdn.amcharts.com/lib/5/element.js"></script>
<script src="https://cdn.amcharts.com/lib/5/percent.js"></script>

Then render the element with its JSON config. The server renders it like any HTML, and the browser builds the chart:

function SalesPage() {
  return () => (
    <am5-chart config={JSON.stringify(config)} data-rmx-preserve-dom></am5-chart>
  );
}

data-rmx-preserve-dom keeps Remix's frame reloads from replacing what the element drew. Without a height, the element is 400 pixels high.

A chart written in code

Make the chart's component a client entry, so it runs in the browser, and create the root with Remix's ref mixin. It gives you the element once it is on the page, and a signal that aborts when it is removed: dispose the root then.

import { clientEntry, css, ref, type Handle } from "remix/component";

export let SalesChart = clientEntry(
  "/assets/sales-chart.js#SalesChart",
  function SalesChart(handle: Handle<{ data: Array<{ category: string, value: number }> }>) {
    return () => (
      <div
        data-rmx-preserve-dom
        mix={[
          css({ height: "400px" }),
          ref(async (node, signal) => {
            // In the browser only
            const am5 = await import("@amcharts/amcharts5");
            const am5percent = await import("@amcharts/amcharts5/percent");
            if (signal.aborted) {
              return;
            }

            const root = am5.Root.new(node as HTMLElement);

            // The signal aborts when the element is removed
            signal.addEventListener("abort", () => root.dispose());

            const chart = root.container.children.push(
              am5percent.PieChart.new(root, {})
            );

            const series = chart.series.push(
              am5percent.PieSeries.new(root, {
                valueField: "value",
                categoryField: "category"
              })
            );

            series.data.setAll(handle.props.data);
          })
        ]}
      />
    );
  }
);
  • The first argument of clientEntry() is where your build serves this module, with the export's name.
  • The library is imported in the ref callback, which runs in the browser only, so the server never loads it.
  • Props of client entries are serialized to JSON: pass data, not functions.

For more on client entries and mixins, see Remix's Hydration and Mixins guides.

License

With a commercial license, call am5.addLicense() before the chart is made: in a script after index.js, or in the ref callback before am5.Root.new().