Subscribe to mailing list

Get notified when we have new updates or new posts!

Subscribe Unicorn Data Science cover image
jen@unicornds.org profile image jen@unicornds.org

React One Day Tour: Making a Simple, Interactive Dashboard

Welcome to our new One Day Tour series! These bite-sized tutorials are designed to give you a high-level overview of a technology that can elevate your data science toolkit. For today's one-day tour, let's get started with building a simple, interactive dashboard in React!

React One Day Tour: Making a Simple, Interactive Dashboard
Photo by Lautaro Andreani / Unsplash

As a data scientist, you've likely been tasked with the challenge of building interactive dashboards. While excellent Python-based solutions like Plotly, Streamlit, FastHTML, and many others exist (and we'll cover these in future one-day tours), today we're diving into React.

Why React? Isn't it enough that Data Scientists already have to be familiar with R, Python, and SQL? Why bring another language (JavaScript) and framework (React) into the mix?

When it comes to deciding which path to upskill, I like to pick something that doesn't just solve the problem at hand, but also opens doors in the future. Currently, React is the most popular web framework. This means an ocean of resources, endless extensibility, and a skill that's in high demand across industries.

So let's embark on this One Day Tour of React - a skill that'll not only be helpful next time you need to build a dashboard, but also come in handy when you are thinking about building other data- and algorithms-driven applications.

What we will be making in today's one day tour.

Prerequisites

If you've already been coding in R and Python, that's it! You probably already have all the prerequisites for today's React tour. More specifically, you should be comfortable with the following:

  • Downloading softwares and running them via the command line
  • Writing code in a text editor of your choice

Let's get started!

1. Installing Node and Vite

Go to https://nodejs.org/en/download/package-manager to download the latest version of Node.js. The expected outcome after installation is that npm and node are now available in your terminal.

After npm becomes available to you, you can install packages with npm install <package-name>.

2. Create a React App

Once upon a time, when you wanted to build a React app, you would scaffold with create-react-app. This creates a new React project with a bunch of ready-to-go files and configurations. There are a lot that happens under the hood (webpack, Babel, ESLint, etc), which is very overwhelming to a new React developer. And Create React App takes care of this so you don't have to worry about it on day one.

Fast forward a few years, create-react-app is no longer the recommended way to get started with a React application. Instead, Vite has entered the scene and became the preferred build tool. Vite doesn't just support React; it also works with other frameworks like Vue (from the same author as Vite!) and Svelte (gaining popularity!).

To create a React app with Vite, use the following command (note, we are specifying "react" as our template):

> npm create vite@latest my-first-react-app -- --template react

You will then see the output like below:

Done. Now run:

  cd my-first-react-app
  npm install
  npm run dev

Follow the instruction on the three commands and you will see a brand new React app running in your browser!

# Navigate to the project directory
> cd my-first-react-app

# Install dependencies as defined in package.json, which was created by Vite using the React template
> npm install

# Run the development server which will open a browser window at http://localhost:5173/ with your app.
# If a browser window doesn't open automatically, just open http://localhost:5173/ in your browser.
> npm run dev
A brand new React app set up with Vite.

3. Editing the App

Right now we are just looking at the default app created by Vite. Let's make it ours with some basic adjustments.

In index.html, we see the following:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

We can change the site title from Vite + React to My First React App. And we can also replace the favicon, vite.svg, with something we like. For example, I'm going to add the Unicorn DS logo into the public folder. If you need to get some free SVG file to play with, I have had good experience with https://www.svgrepo.com/.

Inside index.html, you will see that it's referring to a "/src/main.jsx" file. And then inside src/main.jsx, you see that it's importing the App component from App.jsx.

// main.jsx
import App from "./App.jsx";

So the meaty part of the app is in App.jsx. For the rest of this tour, we will be focusing on this file.

The App.jsx file is not very long:

import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

We can recognize that it's showing two logos, one for Vite and one for React. Then it's displaying some text. There is a button, and then some more text.

Let's first replace the default logos again with something of our choice. Then we can also update the text. After these little changes, the App.jsx might look like:

import { useState } from "react";
import unicornDsLogo from "/unicornds.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://www.unicornds.org/" target="_blank">
          <img src={unicornDsLogo} className="logo" alt="UnicornDS logo" />
        </a>
      </div>
      <h1>My First React App</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          I am changing the code in <code>src/App.jsx</code>.
        </p>
      </div>
      <p className="read-the-docs">React is fun!</p>
    </>
  );
}

export default App;
Editing the default Vite template.

4. Install a React Plotting Library

We want to visualize some data, so let's install a plotting library. In python, you probably already use matplotlib or seaborn on a daily basis. In React, there are many options. For simplicity, we will use Recharts for now, but you are free to explore the sea of npm packages.

Typically, when considering which library to use, you should take a look at the following:

  • Number of weekly downloads: The more the better
  • When was the last update: The more recent and frequent the better
  • License: Is it MIT or other permissive license? This matters less for a learning project, but an important consideration if you are looking for packages to use in production.
The npm registry is a treasure trove of packages ready to use. There are so many options, picking a library can put you in decision paralysis. But you can reference package stats, and even the activity level in the source repository.

All you need to do to introduce Recharts into your project is to run the following in the project directory:

> npm install recharts

5. Generate Some Data and Visualize It

There are many types of data we can visualize. To keep the focus on the React part, we will just create a random walk data:

function generateRandomWalkData(steps = 20, volatility = 10, startValue = 0) {
  let valueAlice = startValue;
  let valueBob = startValue;
  const data = [
    {
      dayNum: 1,
      alice: valueAlice,
      bob: valueBob,
    },
  ];

  for (let i = 1; i < steps; i++) {
    valueAlice += volatility * (Math.random() - 0.5);
    valueBob += volatility * (Math.random() - 0.5);
    data.push({
      dayNum: i + 1,
      alice: valueAlice,
      bob: valueBob,
    });
  }

  return data;
}

const data = generateRandomWalkData();

// You can look at console logs by opening the Developer Tools in your browser. Developer Tools are extremely useful!
console.log(data);

Then we can use Recharts' <LineChart /> component to visualize the data by modifying the App.jsx file. Now the entire file might look like:

import { useState } from "react";
import unicornDsLogo from "/unicornds.svg";
import {
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
} from "recharts";
import "./App.css";

function generateRandomWalkData(steps = 20, volatility = 10, startValue = 0) {
  let valueAlice = startValue;
  let valueBob = startValue;
  const data = [
    {
      dayNum: 1,
      alice: valueAlice,
      bob: valueBob,
    },
  ];

  for (let i = 1; i < steps; i++) {
    valueAlice += volatility * (Math.random() - 0.5);
    valueBob += volatility * (Math.random() - 0.5);
    data.push({
      dayNum: i + 1,
      alice: valueAlice,
      bob: valueBob,
    });
  }

  return data;
}

const data = generateRandomWalkData();

console.log(data);

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://www.unicornds.org/" target="_blank">
          <img src={unicornDsLogo} className="logo" alt="UnicornDS logo" />
        </a>
      </div>
      <h1>My First React App</h1>
      <div className="card">
        <LineChart
          width={600}
          height={320}
          data={data}
          margin={{
            top: 16,
            bottom: 16,
          }}
        >
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="dayNum" />
          <YAxis />
          <Tooltip />
          <Legend />
          <Line
            type="monotone"
            dataKey="alice"
            stroke="#8884d8"
            activeDot={{ r: 8 }}
          />
          <Line type="monotone" dataKey="bob" stroke="#82ca9d" />
        </LineChart>

        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  );
}

export default App;

Viola! We have a plot in our React app.

Now that there's a plot in your React app, you have a data-driven application.

6. React Components and States

Now let's dig into the mechanics of React a bit.

Components and states are two fundamental concepts in user interface development. Components are reusable pieces that help organize and structure code. States represent data that can change, affecting how the interface looks and behaves.

In React, we typically organize components like how we'd organize a Python function: a component is a modular piece of code that represents a user interface element. For example, we can extract out the <LineChart /> component into its own function to make the code more readable:

import { useState } from "react";
import unicornDsLogo from "/unicornds.svg";
import {
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
} from "recharts";
import "./App.css";

function generateRandomWalkData(steps = 20, volatility = 10, startValue = 0) {
  let valueAlice = startValue;
  let valueBob = startValue;
  const data = [
    {
      dayNum: 1,
      alice: valueAlice,
      bob: valueBob,
    },
  ];

  for (let i = 1; i < steps; i++) {
    valueAlice += volatility * (Math.random() - 0.5);
    valueBob += volatility * (Math.random() - 0.5);
    data.push({
      dayNum: i + 1,
      alice: valueAlice,
      bob: valueBob,
    });
  }

  return data;
}

const data = generateRandomWalkData();

console.log(data);

function LineChartRandomWalk() {
  return (
    <LineChart
      width={600}
      height={320}
      data={data}
      margin={{
        top: 16,
        bottom: 16,
      }}
    >
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="dayNum" />
      <YAxis />
      <Tooltip />
      <Legend />
      <Line
        type="monotone"
        dataKey="alice"
        stroke="#8884d8"
        activeDot={{ r: 8 }}
      />
      <Line type="monotone" dataKey="bob" stroke="#82ca9d" />
    </LineChart>
  );
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://www.unicornds.org/" target="_blank">
          <img src={unicornDsLogo} className="logo" alt="UnicornDS logo" />
        </a>
      </div>
      <h1>My First React App</h1>
      <div className="card">
        <LineChartRandomWalk />
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  );
}

export default App;

This code has no functional difference from the previous version. But now that LineChartRandomWalk is its own function, it's easier to reuse it in other places. You might even create a new file with this function for further organization.

Finally, let's talk about states, which is probably the most important topic in React. Handling states well, then you can build complex yet efficient apps. Handling states poorly, your app can have wrong behaviors and suffer from degraded performance, i.e., via unnecessary re-renders.

You might notice there is a line in App.jsx that comes with the default Vite template: const [count, setCount] = useState(0);. There is a reason this line is included - because useState is a core React hook. It defines a state variable, as well as a function to update the state.

To get more comfortable with React state, let's simply rename the state variable to numDays:

const [numDays, setNumDays] = useState(0);

And for the button which originally increments the count, let's update it too, as well as the text of the button:

<button onClick={() => setNumDays((numDays) => numDays + 1)}>
  Showing data for {numDays} days.
</button>

To make the numDays state variable have some effect to the plot, let's pass it into the LineChartRandomWalk component as a prop. "Prop" is short for "property", and a component can accept props as arguments, which then controls how the component will be rendered. For us, we will show only data up to numDays, so we plot only data.slice(0, numDays), the equivalent of data[:numDays] in python. This is what your App.jsx file might look like after these changes:

import { useState } from "react";
import unicornDsLogo from "/unicornds.svg";
import {
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
} from "recharts";
import "./App.css";

function generateRandomWalkData(steps = 20, volatility = 10, startValue = 0) {
  let valueAlice = startValue;
  let valueBob = startValue;
  const data = [
    {
      dayNum: 1,
      alice: valueAlice,
      bob: valueBob,
    },
  ];

  for (let i = 1; i < steps; i++) {
    valueAlice += volatility * (Math.random() - 0.5);
    valueBob += volatility * (Math.random() - 0.5);
    data.push({
      dayNum: i + 1,
      alice: valueAlice,
      bob: valueBob,
    });
  }

  return data;
}

const data = generateRandomWalkData();

console.log(data);

function LineChartRandomWalk({ numDays }) {
  return (
    <LineChart
      width={600}
      height={320}
      data={data.slice(0, numDays)}
      margin={{
        top: 16,
        bottom: 16,
      }}
    >
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="dayNum" />
      <YAxis />
      <Tooltip />
      <Legend />
      <Line
        type="monotone"
        dataKey="alice"
        stroke="#8884d8"
        activeDot={{ r: 8 }}
        isAnimationActive={false}
      />
      <Line
        type="monotone"
        dataKey="bob"
        stroke="#82ca9d"
        isAnimationActive={false}
      />
    </LineChart>
  );
}

function App() {
  const [numDays, setNumDays] = useState(0);

  return (
    <>
      <div>
        <a href="https://www.unicornds.org/" target="_blank">
          <img src={unicornDsLogo} className="logo" alt="UnicornDS logo" />
        </a>
      </div>
      <h1>My First React App</h1>
      <div className="card">
        <LineChartRandomWalk numDays={numDays} />
        <button onClick={() => setNumDays((numDays) => numDays + 1)}>
          Showing data for {numDays} days.
        </button>
      </div>
    </>
  );
}

export default App;
Data? Check! Interactivity? Check! All in <100 lines! Now you have an interactive plot in a React app! Not bad for a short one-day tour.

What's Next?

We have barely scratched the surface of React. But you are now equipped with the fundementals to continue your journey. If you'd like to build up on what we have today, here are some additional exercises you can try:

  • Move the LineChartRandomWalk() component into its own file, and import it into App.jsx.
  • Add another button that decreases numDays.
  • Load your own data instead of using the generateRandomWalkData() function.
  • Use other chart components from Recharts, such as BarChart, PieChart, and ScatterChart.
  • Use another plotting library altogether!

Congratulations! You've just completed a whirlwind tour of React, creating an interactive plot from scratch. While there is still a lot to learn, what I hope to show is that the world of React is not as intimidating as you might've thought. It's possible to become productive with React, and leverage it for your data science projects.

In our future one-day tours, we will introduce other helpful tools, such as how to deploy your React app to the web, how to integrate with external API's such as LLM providers, and how to use common cloud services like AWS and GCP. Stay tuned!