paint-brush
Build a Real-Time Crypto Tracker with CoinGecko API and React.jsby@ileoami
206 reads

Build a Real-Time Crypto Tracker with CoinGecko API and React.js

by IleolamiJuly 19th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Discover how to create a real-time cryptocurrency tracker using CoinGecko's API and React.js. This guide walks you through setting up components, fetching data, and building a functional UI for tracking and displaying crypto information.
featured image - Build a Real-Time Crypto Tracker with CoinGecko API and React.js
Ileolami HackerNoon profile picture


CoinGecko is a comprehensive cryptocurrency data platform that provides real-time and historical data on thousands of cryptocurrencies. It offers a market-tracking website and app for digital currencies. CoinGecko collects data to rank the potential of each coin, benchmarking digital currencies like Bitcoin, Ethereum, and over 6,000 other cryptocurrencies. In short, think of CoinGecko as a library that provides information on various cryptocurrencies. It offers a wide range of information, including price, volume, market capitalization, and more, as seen below:


Cryptocurrency prices by market cap


Trending coin and Largest gainers


popular coins with their details


As CoinGecko offers these services, it also extends its capabilities to developers and organizations through its Application Programming Interface (API), allowing them to use the vast amount of available data. This API provides access to real-time and historical data, enabling the creation of applications that can track cryptocurrency prices, analyze market trends, and more.


CoinGecko's API offers features and endpoints like Market Data, Historical Data, Exchange Data, Global Data, Trending Coins, NFTs Data, and more.


Some of the applications that can be built with CoinGecko's API include:

  1. Crypto Tracker
  2. Prices Checker
  3. Trading Bot
  4. Dashboard.


In this article, you'll learn how to build a Crypto Tracker using React.js for the User Interface (UI).

Prerequisites

  • Basic understanding of JavaScript, ReactJs, and CSS
  • Node.js and npm installed
  • Familiarity with REST APIs

Installed the following:

  1. Reactjs either Create React App or Vite.js
  2. Axios
  3. React Router Dom
  4. Recharts
  5. TailwindCSS or Bootstrap (included in the code)
  6. Code Editor


Setting up the tracker’s components


  1. Clear out App.jsx file to have something like: Empty App.jsx fileThis file will be handling all the routing.
  2. Clear out Index.css and App.css
  3. Create a folder inside src folder and name it Component. This will be where the display file for Coins’ details and the Search field will be stored.
  4. Create two files inside Component and name them Display.jsx and Search.jsx

The component folder contains Display.jsx and Search.jsx files.


  1. Create another component under src folder and name it Homepage.jsx

Homepage.jsx

Displaying Content

To display these components, you will be using the React Router Dom functions such BrowserRouter and Routes and importing them into App.jsx in this manner 👇🏽


import './App.css'
import DisplayCoin from './Component/Display
import Homepage from './Homepage
import { BrowserRouter, Route, Routes} from 'react-router-dom'
function App() {
return ( <BrowserRouter> <Routes> <Route path="/" element={<Homepage />} /> <Route path="/coin/:id" element={<DisplayCoin />} /> //'/coin/:id' to display the details of coin using its id </Routes> </BrowserRouter>
) }
export default App


Fetching Data from CoinGecko API

React Hooks (useState, useEffect)

In this tutorial, you will be making use of useState and UseEffect

  1. useState: This hook is used for adding state management to functional components. In the context of Homepage.jsx, useState will be used to manage the state of coin or any other data that needs to be displayed or updated in the UI. For example, you will fetch coin data from an API and store it in a state variable so that it can be displayed dynamically in the component.


  2. useEffect: This hook is used for performing side effects in functional components. Side effects could be data fetching, subscriptions, or manually changing the DOM in React components. In this case, useEffect will be used to fetch the coin data from an API when the component mounts. It ensures that the data fetching logic is kept separate from the UI logic, and it can also handle re-fetching data if any dependencies change.


Having this understanding, Open Homepage.jsx and import useState and useEffect into the file in this manner👇🏽


import React, { useState, useEffect } from 'react'

const Homepage = () => {

   return (
   
    <div> Crypto Tracker </div>

) }

export default Homepage;


CoinGecko API Endpoints

  1. Coin Data by ID: This endpoint allows you to query all the coin data of a coin (name, price, market .... including exchange tickers) on the CoinGecko coin page based on a particular coin ID.
  2. endpoint: https://api.coingecko.com/api/v3/coins/{id}
  3. Coin historical Chart Data by ID: This endpoint allows you to get the historical chart data of a coin including time in UNIX, price, market cap and 24hrs volume based on a particular coin id.
  4. endpoint: https://api.coingecko.com/api/v3/coins/{id}/market_chart
  5. Search Queries: This endpoint allows you to search for coins, categories and markets listed on CoinGecko.
  6. endpoint: https://api.coingecko.com/api/v3/search
  7. Trending Search List: This endpoint allows you to query trending search coins, nfts and categories on CoinGecko in the last 24 hours.endpoint: https://api.coingecko.com/api/v3/search/trending


Fetching Trending Coin

To fetch the trending coin, we will be making using axios, useEffect, useState and Trending Search list endpoint.


import React, { useState, useEffect } from 'react'

import axios from 'axios'

import { Link } from 'react-router-dom'

const Homepage = () => {

const [trendingCoins, setTrendingCoins] = useState([]);

useEffect(() => {
    const fetchData = async () => {
        try {
            const response = await axios.get('https://api.coingecko.com/api/v3/search/trending');

            console.log(response.data);
    
        } catch (error) {
            console.error(error);
        }
    }
    fetchData();
}, []);

return ( <div> Crypto Tracker </div> )

}

export default Homepage;


You will see the response in the console👇🏽


Trending API response


  • const [trendingCoins, setTrendingCoins] = useState([]);
  • This line initializes a state variable trendingCoins with an empty array. setTrendingCoins is the function used to update this state.
  • The useEffect hook is used to fetch data from an external API (https://api.coingecko.com/api/v3/search/trending) when the component mounts. The empty dependency array [] means this effect runs once after the initial render.
  • Inside useEffect, an asynchronous function fetchData is defined and immediately called. This function uses axios.get to make a GET request to the CoinGecko API to fetch trending coins.
  • If the request is successful, the response data (response.data.coins) is logged to the console and used to update the trendingCoins state with setTrendingCoins.
  • If there's an error during the fetch operation, it's caught in the catch block and logged to the console.


From the above picture, you can see the list of Trending Coins, Categories, and NFTs. In this case, you only need the trending coins’ details. To do this, you just need to add setTrendingCoins(response.data.coins);


import React, { useState, useEffect } from 'react'

import axios from 'axios'

import { Link } from 'react-router-dom'

const Homepage = () => {

const [trendingCoins, setTrendingCoins] = useState([]);

useEffect(() => {
    const fetchData = async () => {
        try {
            const response = await axios.get('https://api.coingecko.com/api/v3/search/trending');
            console.log(response.data);

           setTrendingCoins(response.data.coins);
    
        } catch (error) {
            console.error(error);
        }
    }
    fetchData();
}, []);

return ( <div> Crypto Tracker </div> )

}

export default Homepage;


Trending Coin UI

import React, { useEffect, useState } from 'react'

import axios from 'axios'

import { Link } from 'react-router-dom';

const Homepage = () => { const [trendingCoins, setTrendingCoins] = useState([]);

useEffect(() => {
    const fetchData = async () => {
        try {
            const response = await axios.get('https://api.coingecko.com/api/v3/search/trending');
            console.log(response.data);
            setTrendingCoins(response.data.coins);
        } catch (error) {
            console.error(error);
        }
    }
    fetchData();
}, []);

return ( <div className=''>

    <h1 className="text-2xl font-bold text-center tracking-wider">TRENDING COINS</h1>
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-4 pl-10">
        {trendingCoins.map((coin, index) => (
        <Link to={`/coin/${coin.item.id}`} key={index} className="max-w-sm rounded overflow-hidden shadow-lg p-4 hover:bg-gray-900 hover:shadow-2xl transition duration-300 ease-in-out">
          <div key={index} className="max-w-sm rounded overflow-hidden shadow-lg p-4">
            <img src={coin.item.thumb} alt={coin.item.name} className="w-20 h-20 mx-auto" />
            <div className='flex flex-col justify-center items-center'>
            <h1 className="text-2xl font-bold">{coin.item.name}</h1>
            <p className="text-gray-600">Price: ${coin.item.data.price.toFixed(3)}</p>
            <p className="text-gray-600">Market Cap Rank: {coin.item.market_cap_rank}</p>
            </div>
          </div>
        </Link>
        ))}
</div>
</div>

) }

export default Homepage


The trendingCoins state variable, which now holds the API response data, is mapped over to dynamically generate a list of coin elements. For each coin in trendingCoins, the following information is displayed:


  • A link (<Link>) that navigates to a detailed page for the coin. The to attribute of the link is dynamically set to /coin/${coin.item.id}, where coin.item.id is the unique identifier for each coin.

  • An image of the coin (<img>), where src is set to coin.item.thumb, representing the coin's thumbnail image URL, and alt is set to coin.item.name, providing a text alternative for the image.

  • The coin's name (<h1>), is displayed in a large, bold font.

  • The coin's price (<p>), where coin.item.data.price.toFixed(3) formats the price to three decimal places. This indicates that coin.item.data.price is a numerical value representing the coin's current price.

  • The coin's market cap rank (<p>), is displayed using coin.item.market_cap_rank. This is a numerical rank indicating the coin's position based on its market capitalization.


Styling and Layout

The coins are displayed in a responsive grid layout, with styling applied for aesthetics (e.g., rounded corners, shadow, hover effects) and responsiveness (e.g., different column counts for different screen sizes).


Trending Coin UI


Coin Chart Data with Price and TimeStamp

This is the graphical illustration that shows the changes in prices within one day. To do this Create another file and name Rechart.jsx inside the Component.

Rechart.jsx file

You will be making use of the:

  1. Rechart package.
  2. useParam hooks from React Router Dom to access the URL parameters, specifically to get the ID of the cryptocurrency.
  3. Coin historical Chart Data by ID endpoint.


Inside the file add the following 👇🏽

import React, { useEffect, useState} from 'react' import axios from 'axios' import { useParams } from 'react-router-dom'; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';

const Rechart = () => { const params = useParams(); const [graphRes, setGraphRes] = useState([]);

useEffect(() => {
    const fetchData = async () => {

        try {

            const response = await axios.get(`https://api.coingecko.com/api/v3/coins/${params.id}/market_chart?vs_currency=usd&days=1`);
            
               setGraphRes(response.data.prices.map((item) => {
                const [timestamp, p] = item;

                const date = new Date(timestamp).toLocaleDateString('en-us')

                return {
                    Date: date,
                    Price: p,
                };
            }));
        } catch (error) {
            console.error(error);
        }
    };

    fetchData();
}, []);

return ( <div> <AreaChart

        width={750}
        height={400}
        data={graphRes}
        margin={{
            top: 10,
            right: 30,
            left: 0,
            bottom: 0,
        }}
    >

        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="Date"/>
        <YAxis />
        <Tooltip />
        <Area type="monotone" dataKey="Price" stroke="#8884d8" fill="#8884d8" />
    </AreaChart>
</div>

) }

export default Rechart


Data Processing

  • The response from the API contains price data as an array of [timestamp, price] pairs.

  • This data is mapped over to transform it into an array of objects with Date and Price properties, where Date is a formatted date string and Price is the cryptocurrency price.

  • The transformed data is then set to the graphRes state using setGraphRes.


Rendering the chart

  • The component returns JSX that renders an AreaChart component from recharts.
  • The AreaChart is configured with a fixed width and height, and it's provided the data from graphRes.
  • Inside the AreaChart, various components configure the appearance and behavior of the chart:
  • CartesianGrid adds a grid to the chart.
  • XAxis and YAxis define the axes of the chart, with Date being used as the data key for the X-axis.
  • Tooltip enables a tooltip that shows information about a data point when hovered.
  • Area defines the area chart itself, specifying that it should be a monotone type for smooth curves, and it sets the data key to Price for plotting, along with stroke and fill colors.

  • Chart Data


Coin Details UI

Recall the Display.jsx component you created at the start of this tutorial; this is where the coin details including coin chart data will be shown when a user clicks on any trending coin or searches for a specific coin.


In this component, you will be making use of :

  1. Rechart Component by importing it
  2. useParams Hook.
  3. Coin Data by ID endpoint


import React, { useEffect, useState} from 'react' import axios from 'axios' import { useParams } from 'react-router-dom'; import Rechart from './Rechart';

const Display = () => { const params = useParams(); const [apiResponse, setApiResponse] = useState([]);

useEffect(() => { const fetchData = async () => { try { const apiResponse = await axios.get(https://api.coingecko.com/api/v3/coins/${params.id}?localization=false); console.log(apiResponse); setApiResponse(apiResponse) } catch (error) { console.error(error); }

};


fetchData();
}, []); return ( <div> {apiResponse && ( <div className=" max-w-screen-md mx-auto rounded-lg overflow-hidden shadow-lg p-6 bg-white"> <h1 className="text-2xl font-bold text-gray-800 mb-4 text-center">{apiResponse.data.name}</h1> <img src={apiResponse.data.image.small} alt={apiResponse.data.name} className="w-80 ml-20 lg:ml-52 mb-4 rounded-lg shadow" /> <Rechart/> <h2 className="font-bold text-lg text-gray-800 mb-2 text-center tracking-widest">DETAILs</h2> <div className=' leading-12 '> <p className="text-gray-600 mb-10 flex justify-between border-b-2">Currency <span className="font-semibold">{apiResponse.data.symbol}</span></p> <p className="text-gray-600 mb-10 flex justify-between border-b-2">Market Cap Rank <span className="font-semibold">{apiResponse.data.market_cap_rank}</span></p> <p className="text-gray-600 mb-10 flex justify-between border-b-2">Market Cap <span className="font-semibold">${apiResponse.data.market_data.market_cap.usd}</span></p> <p className="text-gray-600 mb-10 flex justify-between border-b-2">Total Supply <span className="font-medium">{apiResponse.data.market_data.total_supply}</span></p> <p className="text-gray-600 mb-10 flex justify-between border-b-2">Circulating Supply <span className="font-medium">{apiResponse.data.market_data.circulating_supply}</span></p> <p className="text-gray-600 flex justify-between border-b-2">Max Supply <span className="font-medium">{apiResponse.data.market_data.max_supply}</span></p> </div> </div> )} </div> ) }

export default Display


Fetching Coin Details

Makes asynchronous GET request to fetch detailed information about the cryptocurrency using Coin Data by ID endpoint. The URL includes a query parameter for disabling localization and a demo API key.


So when the users click on any coin, they should see a UI like this 👇🏽

Display UI

Search field

Here, the user will be able to search for any cryptocurrencies and there will be list of cryptocurrencies related to the inputed text. For this functionality, you will be using:

  1. Search Queries
  2. Javascript Debounce functionality


Add the following code to the file👇🏽

import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { Link } from 'react-router-dom';

const Search = () => { const [apiResponse, setApiResponse] = useState(null); const [search, setSearch] = useState('');

const debounce = (func, delay) => {
    let inDebounce;
    return function() {
        const context = this;
        const args = arguments;
        clearTimeout(inDebounce);
        inDebounce = setTimeout(() => func.apply(context, args), delay);
    };
};

const handleSearch = async () => {
    if (!search) {
        setApiResponse(null);
        return;
    }
    try {
        const response = await axios.get(`https://api.coingecko.com/api/v3/search?query=${search}`);
        console.log(response.data);
        setApiResponse(response.data);
    } catch (error) {
        console.error('Error:', error);
    }
};

const debouncedSearch = debounce(handleSearch, 500);

useEffect(() => {
    debouncedSearch();
}, [search]);

return (
    <div className="p-4">
        <input
            type="text"
            value={search}
            placeholder="Search for a coin"
            onChange={(e) => setSearch(e.target.value)}
            className="w-full h-12 my-4 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        {apiResponse && (
            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-4">
                {apiResponse.coins.map((coin, index) => (
                    <div key={index} className="max-w-sm rounded overflow-hidden shadow-lg p-4">
                        <Link to={`/coin/${coin.id}`} className="text-blue-500 hover:text-blue-700">{coin.name}</Link>
                    </div>
                ))}
            </div>
        )}
    </div>
);

};

export default Search;


  1. State Management: It uses the useState hook to manage:
  • apiResponse: Stores the response from the CoinGecko API.
  • search: Stores the current value of the search input field.
  1. Debounce Function: A debounce function is defined to limit the rate at which a function (handleSearch) is executed. This is use to optimizing performance and reducing the number of API calls made while typing in the search input.
  2. handleSearch Function: This asynchronous function is triggered to call the CoinGecko API. It checks if the search state is not empty, then makes a GET request to the CoinGecko API with the search query. The response is logged to the console and stored in the apiResponse state. If an error occurs, it's logged to the console.
  3. Debounced Search: The debounce function is applied to handleSearch with a 500ms delay, creating a new function debouncedSearch. This means handleSearch will only be called if there's a 500ms pause in invoking debouncedSearch, effectively reducing the number of API calls during rapid typing.
  4. useEffect Hook: React's useEffect hook is used to call debouncedSearch whenever the search state changes. This means the API call will be made 500ms after the user stops typing.
  5. Rendering: The component renders:
  • An input field for the user to type their search query. The input's value is bound to the search state, and it updates the search state on change.
  • A conditional block that checks if apiResponse is not null. If true, it maps over apiResponse.coins and renders a list of coins. Each coin is wrapped in a Link component for navigation, with the coin's name displayed as a link.
  1. After import the Search component into your Homepage.jsx,


Complete homepage UI


Search Response


Congratulations, You have successfully built your own crypto Tracker🍾👏🏽


Conclusion

In this article, you learned about the CoinGecko API, including its key features and endpoints, and how to use Axios to fetch data. You also explored examples of applications you can build with the API and followed a step-by-step guide to creating a complete application.

Further Resources

CoinGecko Documentation

More features in the Crypto Tracker


If you find this article helpful, let me know in the comment section, like✅, share 🛂 and follow 🚶🏽‍♂️for more.