Recharts and InfluxDB Tutorial - Visualize IoT Sensor Data with ReactJS

Navigate to:

In this tutorial, you will learn how to create a custom data visualization with ReactJS using the Recharts charting library to display time series data stored with InfluxDB. To do this you will store some real-time data being recorded by some IoT sensors which record the temperature, humidity, and carbon monoxide levels of a room.

By the end of this post, you will have a solid understanding of the fundamentals of using Recharts and an introduction into how to work with InfluxDB to write and read time series data.

What is Recharts?

Recharts is a popular charting and data visualization library used with ReactJS. Recharts was initially released in 2016 and was one of the first charting libraries designed explicitly for use in ReactJS apps, with the library itself being built using React and D3js under the hood.

Like most great open source projects, Recharts was designed after facing real problems and pain points while building a business application. The main issue at the time was trying to integrate established JavaScript charting libraries into a ReactJS app, which was proving to be awkward. By creating a charting library in React itself, developers would be able to take advantage of React’s virtual DOM to make charts faster, use React’s native component lifecycle system, and make customizing charts easier. Some of the best features provided by Recharts are:

  • 11 different types of charts
  • Easy-to-understand API
  • Decoupled component architecture
  • Flexible and easy to customize
  • Strong community makes it easy to solve any problems

The main goals of Recharts is to make it simple to create charts in ReactJS applications using normal React components, provide native SVG support, and have a declarative API for components. It’s safe to say Recharts succeeded in those goals and as a result now has over 17,000 stars on GitHub, hundreds of contributors, and many companies using it in production. All of this makes Recharts a safe bet to use in your own apps.

What is InfluxDB?

InfluxDB is a NoSQL time series database, meaning that InfluxDB is optimized for storing, querying, and analyzing time series data. Time series data is a sequence of data points indexed in time order. Some common examples of time series data are:

In addition to being able to efficiently store and query time series data, InfluxDB acts as a platform of tools for working with your data. This includes functionality for creating serverless tasks and alerts based on your data, pre-built functions for common time series data analysis using the Flux query language, and easy-to-use dashboards for displaying data.

Getting started with Recharts

Before getting into the tutorial itself, I’ll do a brief introduction to the Recharts library and cover some high-level concepts that will be useful for using Recharts beyond the use case covered in this specific tutorial.

Chart data format

For your charts to display properly, you will need to provide data to the chart as a prop. The structure of the data will generally be an array of objects, with each object representing a single data point. Here is an example data value for a bar chart:

const data = [
  {
    name: 'Page A',
    uv: 4000,
    pv: 2400,
    amt: 2400,
  },
  {
    name: 'Page B',
    uv: 3000,
    pv: 1398,
    amt: 2210,
  },
  {
    name: 'Page C',
    uv: 2000,
    pv: 9800,
    amt: 2290,
  },
  {
    name: 'Page D',
    uv: 2780,
    pv: 3908,
    amt: 2000,
  },
  {
    name: 'Page E',
    uv: 1890,
    pv: 4800,
    amt: 2181,
  },
  {
    name: 'Page F',
    uv: 2390,
    pv: 3800,
    amt: 2500,
  },
  {
    name: 'Page G',
    uv: 3490,
    pv: 4300,
    amt: 2100,
  },
];

You will map these object keys to Recharts using properties in your code

Chart props

Each chart type has a few unique props that can be passed to customize it, but there are also some common properties that are available to all charts that are worth knowing:

  • Data: The entire `data` object needs to be passed to the parent chart component
  • DataKey: The `dataKey` prop
  • Height/Width: Height and width will determine the size of your chart with a fixed number of pixels. If you want a responsive chart Recharts also provides a responsive container component that will wrap your chart component and take up a percentage of space available
  • Stroke/Fill: These props are used to define the color of the line/bar of your chart.

The structure of a Recharts chart component

Recharts is designed around the idea of everything being a component. As a result, you will often be nesting components to get the chart you want. For example, tooltips, grids, and chart axis are all added to charts by nesting the provided components into the chart parent element.

The highest level component provided by Recharts is the ResponsiveContainer component which can be used to wrap charts so that they can have  a responsive height and width.

Some of the most commonly used utility components in the Recharts library are:

  • Tooltip - Can be used to show the exact value of a data point when the user hovers over it. Formatting is completely customizable
  • Legend - Component for displaying the unit names/values of your chart
  • YAxis/XAxis - Component for adding axis to your chart

Basic Line Chart with Recharts

Enough of the theoretical stuff: time to look at some real code for implementing a simple line chart with Recharts:

import {
  LineChart,
  Line,
  Tooltip,
  CartesianGrid,
  YAxis,
  Legend
} from "recharts";

const data = [
  {
    name: "requests",
    count: 1500
  },
  {
    name: "requests",
    count: 1720
  },
  {
    name: "requests",
    count: 1650
  },
  {
    name: "requests",
    count: 1510
  },
  {
    name: "requests",
    count: 1400
  }
];
export const BasicLine = () => {
  return (
    <LineChart width={400} height={400} data={data}>
      <Line type="monotone" dataKey="count" />
      <CartesianGrid />
      <YAxis />
      <Legend verticalAlign="top" height={30} />
      <Tooltip />
    </LineChart>
  );
};

Basic Line Chart with Recharts

I placed this chart in a newly created components folder and then imported it into the main App.js file to be rendered by React. This is pretty bare bones but gives you a basic example of how you will work with Recharts. The key thing to note is the dataKey prop which is used to define what the value of the line chart will be. If an object has multiple keys you still have control over which one is used for the chart value. Be sure to check out the documentation and play around with the line chart props to get a better feel for Recharts before moving on to the main project.

Set up InfluxDB

This tutorial will involve storing time series data in InfluxDB and then querying that data to visualize it with Recharts. You can create a free tier InfluxDB Cloud account in a few minutes or download and install the open source version.

Once you have your running InfluxDB instance, you need to follow these steps to get set up for using the InfluxDB API using the JavaScript client library:

  1. Create an organization - This will be done while creating your account, the name of your organization can be found in your user settings if you want to change it or forget it.
  2. Create a bucket for storing data - the bucket name will be used in our code to query data.
  3. Create API token for your bucket - Once you have created your bucket, you need to create an API token which will be used to authenticate the API requests made from the client library.

Install project requirements

To follow this tutorial, you will need a NodeJS development environment setup. Create React App will be used as the starting template for the project but you can use whatever setup you feel most comfortable with. If you don’t want to set up anything on your computer locally you can use something like Codesandbox or Replit to create a development environment in your browser. Both services are free and offer React templates so you can get started in seconds.

If you are developing locally and use VS Code as your editor, you can try out the Flux VS Code extension which will give you syntax highlighting, autocompletion, and direct integration with InfluxDB inside VS Code. This will make testing and running your InfluxDB queries easier, so check it out.

This tutorial requires the InfluxDB JavaScript client library and Recharts, which can be installed with the following command:

npm install @influxdata/influxdb-client recharts

You also need to grab the API token you created earlier and either store it as an environment variable or as a variable in your code. Be aware that this is a security concern and should not be used in production because your API token will be embedded in your public frontend source code in the browser.

In a production environment, you would want to handle these API calls server-side so your token is secure. You can see an example of this being done in our Highcharts tutorial. Another option is to give your API token read-only access on the client and set a short lifespan so it expires quickly.

Write data to InfluxDB

Next, you need to write some data to InfluxDB so you have something to query and visualize later in the tutorial. InfluxDB provides a number of ways to write data:

  • Telegraf - Telegraf is an open source server agent created by InfluxData that is used to collect, process, and then output data to storage without requiring any code. Telegraf has over 250 input plugins for data sources and can output data to 40 different data stores. You can follow this getting started tutorial if you are interested in learning how to use Telegraf.
  • File upload - The InfluxDB UI provides the ability to upload CSV or text files that are formatted in Line Protocol. You can also type data directly into the UI and submit a few data points if you want to quickly test some data.
  • Client Libraries and REST API - InfluxDB provides access to all functionality directly through its REST API to give developers maximum flexibility in how they store and query data. Client libraries are also available for 13 different programming languages. These libraries act as a wrapper around the REST API and give developers a bunch of features to make developing apps with InfluxDB easier.
  • Sample data - InfluxDB includes a few sample datasets which can be written to a bucket using Flux.

For this tutorial, we will be using the sample datasets to reduce complexity. There are several different time series datasets available, but for this tutorial we will be using the Air Sensor data which gives temperature, humidity, and carbon monoxide sensor data for multiple rooms in a building.

This data is updated every 15 minutes, so to make sure your data stays recent you will need to create an InfluxDB task that will update the data in your bucket every 15 minutes. Click on the Tasks icon on the sidebar and then click “Create Task”.

Give your task a name and then set the “Every” column value to ‘15m’ so that the task will run every 15 minutes. Paste the following Flux code into the editor:

import "influxdata/influxdb/sample"

sample.data(set: "airSensor")
  |> to(bucket: "your-bucket-name"  )

Make sure to change the bucket name to the bucket you created earlier.

Change the bucket name

Click Save and go back to the tasks dashboard page. Click on the wheel icon for the task you created and click Run so that it runs right away, so you don’t have to potentially wait 15 minutes.

Create-Task

Now go back to the Data Explorer and check your bucket. If you followed all the steps, you should see your data when you submit a query. If you don’t see any data, try refreshing your browser and double check you are looking at the right bucket. If you still don’t see any data, try looking at the documentation and make sure you followed all the previous steps and didn’t miss anything.

Query data from InfluxDB

Now that we have some data stored, we can start making queries. In this tutorial, you will be using Flux, a query language designed for working with time series data from the ground up. Flux provides a number of built-in functions for working with time series data to make your life easier as a developer. It shares some similarities with JavaScript in terms of syntax so it doesn’t take long to pick up the basics.

The easiest way to get started with querying data stored in InfluxDB is to use the Data Explorer from the user interface. You can visually modify queries and see how the data changes in real-time. You can then switch over from the visual query builder and see what the actual underlying Flux query looks like. Playing around with data and then seeing the query is probably the fastest way to Flux. For a full introduction and overview of what Flux can do, be sure to read the documentation.

At a high level, Flux queries have 4 main pieces:

  1. Source - The specific bucket which is being queried
  2. Filter - In most cases, you will only need portion of the data in your bucket, so you will filter based on a range of time and maybe on select a few fields that you need from each data point.
  3. Shape - At this stage you might want to modify how the data is formatted before further processing. This can include things like pivoting columns into rows, dropping columns, or grouping the data by keys.
  4. Process - The final stage is processing the data. Some examples of this would be aggregating the remaining values, selecting a specific minimum or maximum value, rewriting or modifying each row value, or analyzing the data and then sending an alert based on the result.

Here’s a simple Flux query that follows the above structure:

from(bucket: "example-bucket")            // ?? Source
  |> range(start: -1d)                    // ?? Filter on time
  |> filter(fn: (r) => r._field == "foo") // ?? Filter on column values
  |> group(columns: ["sensorID"])         // ?? Shape
  |> mean()                               // ?? Process

Visualize data with Recharts

With all the setup out of the way we can finally get to the good stuff. For this section we will take advantage of Recharts ComposedChart component which lets us combine multiple different types of charts together. The result will be a line chart and area chart which show us the humidity and temperature for a specific room which is being tracked by an IoT sensor.

The first thing you will want to do is create a new component in the components folder you created earlier. You can name it whatever you want but I’ll be naming mine InfluxChart, which is how it will be imported into the App.js file.

Here is the code for the chart component. I’ll walk through it step by step below:

import React, { useState, useEffect } from "react";
import { InfluxDB } from "@influxdata/influxdb-client";
import {
  ComposedChart,
  Bar,
  Line,
  XAxis,
  YAxis,
  Tooltip,
  Legend,
  CartesianGrid,
  Area
} from "recharts";

const token = "your-API-token";
const org = "org-name";
const url = "INFLUX_DB_URL";

let query = `from(bucket: "air-sensor")
  |> range(start: -30m)
  |> filter(fn: (r) => r["_measurement"] == "airSensors")
  |> filter(fn: (r) => r["_field"] == "humidity" or r["_field"] == "temperature" )
  |> filter(fn: (r) => r["sensor_id"] == "TLM0102")
  |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
  |> yield(name: "results")
`;

export const InfuxChart = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    let res = [];
    const influxQuery = async () => {
      //create InfluxDB client
      const queryApi = await new InfluxDB({ url, token }).getQueryApi(org);
      //make query
      await queryApi.queryRows(query, {
        next(row, tableMeta) {
          const o = tableMeta.toObject(row);
          //push rows from query into an array object
          res.push(o);
        },
        complete() {
          let finalData = []

          for (let i = 0; i < res.length; i++) {
            let point = {};
            point["humidity"] = res[i]["humidity"];
            point["temperature"] = res[i]["temperature"];

            point["name"] = res[i]["_time"];
            finalData.push(point);
          }
          setData(finalData);
        },
        error(error) {
          console.log("query failed- ", error);
        }
      });
    };

    influxQuery();
  }, []);
  return (
    <div>
      <h1>Influx Chart</h1>
      <ComposedChart width={900} height={400} data={data}>
        <CartesianGrid />
        <Tooltip />
        <Line
          stroke="#0ff770"
          strokeWidth={1}
          dataKey="temperature"
          dot={false}
        />
        <Area stroke="#bf04b3" fill="#f235e6" dataKey="humidity" />
        <XAxis hide dataKey="name" />
        <YAxis />
      </ComposedChart>
    </div>
  );
};

And here is the resulting chart:Visualize data with Recharts - resulting chart

So here is what is going on in the code above:

  • Flux Query - The Flux query first grabs all the sensor data stored in our bucket in the last 30 minutes using the range function. That data is then filtered to only grab the airSensors measurement, filtered again to grab only the humidity and temperature fields, and then filtered a final time to grab only the data from the sensor with ID TLM0102. Finally the pivot function is used to combine the humidity and temperature field values based on timestamp so they are a single row value.
  • useEffect - Inside the chart component the most complicated code is the useEffect hook which makes the API call to InfluxDB using the client library. The results from the query are appended to the res result array. Once the call has returned the code inside complete runs. Here we loop over the result array and modify the results before adding them to the finalData array. We then use setData which is provided by a React useState hook to set the data into our component's state so the chart can display the data.
  • Chart component - The JSX for the chart itself is fairly simple, the data is passed into the data prop and the Line and Bar components each take one of the values to display.

The App.js file is simple, we are just importing our chart component which was placed inside a components folder:

import { InfluxChart } from "./components/InfluxChart";
export default function App() {
  return (
    <div className="App">
      <InfluxChart />
    </div>
  );
}

So you have a few basic charts setup but it’s kind of boring, what’s the next step? The good news is that this project can be extended in a number of different directions. You can dive deeper into what you are doing with Recharts or InfluxDB. Here are a couple ideas:

  • Custom chart event handlers - Recharts allows you to pass custom functions to every component in your chart to handle various events like mouse hovers, clicks, and when the mouse exits or enters the area of the component. This gives you a lot of options for customizing the behavior of your chart.
  • Connect a different data source - The sample data isn't the most exciting data to work with, the good news is that there are tons of different options available. You can check out any of Telegraf's 250+ integrations and find a data source that looks like fun to mess around with.
  • Dynamic Charts - Because Recharts is just like any other React component you can compose elements like lines or bars inside the main chart component. Using the sensor data we used in this tutorial as an example, you could setup your chart so that they still work as more rooms have sensors added, rather than being limited to a static value.
  • Set up alerts or automated tasks - You already created a task to make sure the air sensor data gets updated every 15 minutes, but you can do much more with InfluxDB tasks. You can set thresholds to monitor in your data and then create automated actions if those thresholds are violated.

Additional resources

Now that you’ve gotten a good foundation when it comes to using InfluxDB and Recharts, I’ll leave you with some resources to check out if you want to take your knowledge to the next level.