How To Create a Chart From a Dynamic Dataset in Next JS Using Chart.js
Published: 2024-01-05 14:12:36

A **chart** is defined as a visual representation of data, and it is important in breaking down data in a way that is simple to understand and interpret. There are various use cases for visualizations in apps e.g. ecommerce dashboards and cryptocurrency dashboards. 

In this tutorial, we shall learn how to create a line chart in Next JS using Chart JS. We shall build on this project that we created [here](https://dev.to/andisiambuku/creating-a-cryptocurrency-app-with-next-js-and-coin-gecko-api-b7h).

## Prerequisites
In this tutorial we shall use the following:

- Node JS installed in your machine.
- Next 13 and above.
- The documentation for the Coin Gecko API is available [here](https://www.coingecko.com/api/documentation). Be sure to refer to the documentation whenever we use an endpoint.
- Tailwind CSS for styling our app.

## Getting Started
Chart JS is a great choice for creating charts as it has multiple visualizations. There are different integrations for the JS frameworks, and we shall use [react-chart-js-2](https://react-chartjs-2.js.org/) library for Next JS.

### Installing Dependencies
We shall install the dependencies by running the following command.
```
npm install --save chart.js react-chartjs-2
```
### Market Chart Component
We shall create a component to handle the fetching and creation of the chart.

In the Components folder, create a folder called `MarketChart.tsx`.

Have the following imports at the top of your file. 

```
"use client";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  Tooltip,
  PointElement,
  LineElement,
} from "chart.js";
import { useEffect, useState } from "react";
import { Line } from "react-chartjs-2";

// Register ChartJS components using ChartJS.register
ChartJS.register(
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Tooltip
);
```
It is imperative to register the correct elements for the chart you are creating, refer to the [documentation](https://www.chartjs.org/docs/latest/getting-started/integration.html) for the elements. The ones placed in the code above are for a line chart.

Next, we proceed to fetching the data required to create the chart.

We shall create a spinner to be shown while data fetching process takes place.
```
  if (!chartData || !chartData.prices) {
    return (
      <div className="flex items-center justify-center h-screen">
        <div className="animate-spin rounded-full border-4 border-solid border-current border-r-transparent h-12 w-12"></div>
      </div>
    );
  }

```

Next, we shall have the logic to create the data structure that Chart.js will use to display the chart along with the properties such as the label, border color, border width, and point radius.
```
  const { prices } = chartData;

  const data = {
    labels: prices.map((entry: any) => new Date(entry[0]).toLocaleDateString()),
    datasets: [
      {
        label: "Price (USD)",
        data: prices.map((entry: any) => entry[1].toFixed(2)),
        borderColor: "orange",
        borderWidth: 2,
        pointRadius: 4,
      },
    ],
  };
```
Finally, we render the line chart by passing in the data attribute in the `Line` component from react-chartjs-2.

```
  return (
    <div>
      <Line data={data} />
    </div>
  );
```

The Market Chart component will be as shown below.

```
"use client";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  Tooltip,
  PointElement,
  LineElement,
} from "chart.js";
import { useEffect, useState } from "react";
import { Line } from "react-chartjs-2";

// Register ChartJS components using ChartJS.register
ChartJS.register(
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Tooltip
);

interface LineProps {
  id: string;
}

const MarketChart: React.FC<LineProps> = ({ id }) => {
  const [chartData, setChartData] = useState<any>(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(
          `https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&days=30&precision=2&interval=daily`
        );
        const data = await response.json();
        console.log("chartData:", data);
        setChartData(data);
      } catch (error) {
        console.log(error);
      }
    };
    fetchData();
  }, [id]);

  if (!chartData || !chartData.prices) {
    return (
      <div className="flex items-center justify-center h-screen">
        <div className="animate-spin rounded-full border-4 border-solid border-current border-r-transparent h-12 w-12"></div>
      </div>
    );
  }
  const { prices } = chartData;

  const data = {
    labels: prices.map((entry: any) => new Date(entry[0]).toLocaleDateString()),
    datasets: [
      {
        label: "Price (USD)",
        data: prices.map((entry: any) => entry[1].toFixed(2)),
        borderColor: "orange",
        borderWidth: 2,
        pointRadius: 4,
      },
    ],
  };

  return (
    <div>
      <Line data={data} />
    </div>
  );
};
export default MarketChart;

```
### Coin Details Component

We shall proceed to plug the Market Chart component we created above to the Coin Details component which will render the respective charts for the coin selected. 

We create a div below the last element of the page and add the Market Chart component.

```
        <div className="py-5">
         <MarketChart id={details.id}/>
        </div>
```
We then pass the `id` of the current coin to allow for dynamic fetching of the market chart.

The whole component will look like this.

```
import { CoinDetails } from "@/types/coinDetails";
import Image from "next/image";
import alt from "@/assets/alt coin.jpg";
import MarketChart from "./MarketChart";

export async function CoinPage({ promise }: { promise: Promise<CoinDetails> }) {
  const details = await promise;

  const content = (
    <div className="p-5 text-gray-700" key={details.id}>
      <div className="flex flex-col justify-center">
        <p className="py-1 px-2 w-24 bg-orange-500 text-white rounded-lg inline-block">
          Rank #{details.market_cap_rank.toLocaleString()}
        </p>
        <div className="flex items-center flex-wrap">
          <Image
            src={details.image.small || alt}
            alt="coin image"
            width={30}
            height={30}
            className="mr-2"
          />
          <h2 className="py-2 text-2xl text-black font-bold">
            {details.name}
            <span className="px-2 text-base font-light text-neutral-400">
              {details.symbol}
            </span>
          </h2>
        </div>
        <p className="py-3 text-xl text-black font-bold">
          ${details.market_data.current_price.usd.toLocaleString()}
        </p>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
          <ul className="py-1">
            <li>
              <span className="font-semibold py-2 mr-2">Market Cap:</span> $
              {details.market_data.market_cap.usd.toLocaleString()}
            </li>
            <li>
              <span className="font-semibold py-2 mr-2">
                Circulating Supply:
              </span>{" "}
              ${details.market_data.circulating_supply.toLocaleString()}
            </li>
            <li>
              <span className="font-semibold py-2 mr-2">
                Fully Diluted Valuation:
              </span>{" "}
              $
              {details.market_data.fully_diluted_valuation.usd.toLocaleString()}
            </li>
            <li>
              <span className="font-semibold py-2 mr-2">
                Market Cap Change (24h):
              </span>{" "}
              {details.market_data.market_cap_change_percentage_24h.toFixed(1)}%
            </li>
          </ul>
          <ul className="py-1">
            <li>
              <span className="font-semibold py-2 mr-2">Maximum Supply:</span> $
              {details.market_data.max_supply?.toLocaleString() || "N/A"}
            </li>
            <li>
              <span className="font-semibold py-2 mr-2">
                Price Change % (24h):
              </span>{" "}
              {details.market_data.price_change_percentage_24h.toFixed(1)}%
            </li>
            <li>
              <span className="font-semibold py-2 mr-2">Total Supply:</span> $
              {details.market_data.total_supply.toLocaleString()}
            </li>
            <li>
              <span className="font-semibold py-2 mr-2">Total Volume:</span> $
              {details.market_data.total_volume.usd.toLocaleString()}
            </li>
          </ul>
        </div>
        <div className="py-3">
          <h3 className="text-xl font-bold ">Information</h3>
          <ul>
            
            <li className="py-1">
              <span className=" mr-2">
                <a
                  href={details.links.homepage[0]}
                  className="hover:underline"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  Website: {details.name}.org
                </a>
              </span>{" "}
            </li>
          </ul>
        </div>
        <div className="py-5">
         <MarketChart id={details.id}/>
        </div>
      </div>
    </div>
  );

  return content;
}

```

Check out the live demo and see the charts in action [here](https://krypto-app-silk.vercel.app/).

## Conclusion
The project is available [here](https://github.com/andisiambuku/Krypto-App). If you wish to contribute, feel free to create a pull request. 
To overcome some challenges such as rate limiting and if you wish to use more features of the Coin Gecko API, use my code CGANDISI500 for $500 of on your API subscription. Sign up [here](https://www.coingecko.com/en/api/pricing) to redeem the discount.

Thank you for reading! Until next time, may the code be with you.