React Highcharts Example with Cube.js
Tags: react, tutorial, webdev, javascript
Published: 2020-07-23 16:01:58

Started as a simple charting tool for monitoring a snow depth near the owner's country house in Norway, Highcharts quickly became one of the most popular visualization libraries. It provides a lot of great built-in interactive features and is easy to use. 

In this tutorial we are going to build a simple e-commerce dashboard with Cube.js and Highcharts. We’ll use the main Highcharts library, as well as Maps, Stock, and Solid Gauge modules.

Please keep in mind that Highcharts libraries are available under different licenses, depending on whether it is intended for commercial/government use, or for personal or non-profit projects. Make sure to check its <a href="https://shop.highsoft.com" target="_blank">license page</a>. 

Below, you can see the demo of the dashboard that we’re going to build.

![Highcharts React Demo](https://dev-to-uploads.s3.amazonaws.com/i/m58xgktr5g8y4w5agce9.gif)

You can find [a live demo here](https://highcharts.cubecloudapp.dev/#/) and [the source code is available on Github](https://github.com/cube-js/cube.js/tree/master/examples/highcharts).

To implement this example, we’ll need:
- Database (we will use PostgreSQL) with sample data
- Cube.js backend to handle communications between our database and the frontend
- The frontend application (we will build one with React)

## Analytics Backend

We’re going to use a PostgreSQL database and an example e-commerce dataset. Use the following commands to download and import the example dataset.

```bash
$ curl http://cube.dev/downloads/ecom-dump.sql > ecom-dump.sql
$ createdb ecom
$ psql --dbname ecom -f ecom-dump.sql
```
Next, let’s install the Cube.js CLI and create a new project.
```bash
$ npm -g install cubejs-cli
$ cubejs create highcharts -d postgres
```
Cube.js uses environment variables inside the `.env` file for configuration. Update the contents of the `.env` file with your own database credentials. 
```bash
CUBEJS_DB_NAME=ecom
CUBEJS_DB_TYPE=postgres
CUBEJS_API_SECRET=SECRET
```
Now, let’s start with the Cube.js backend.
```bash
$ npm run dev
```
At this step, you can find the Cube.js playground at [http://localhost:4000](http://localhost:4000/).

Here, you can see all the tables from our database and we can choose any of them to generate the schema. 

[The Cube.js Data Schema](https://cube.dev/docs/getting-started-cubejs-schema) concept is based on multidimensional analysis and should look familiar to those with experience in OLAP cubes. 

The two main entities are measures and dimensions:
[dimensions](https://cube.dev/docs/dimensions) are ”as is” properties that we get from our database, but [measures](https://cube.dev/docs/measures#top) are results of aggregation operations like count, sum, average, and others. 

In this example, we need an `orders` and `users` table. Please, check it and click “Generate Schema.” Cube.js will then generate `Orders.js` and `Users.js` files inside the `schema` folder.

![Cube.js Schema](https://dev-to-uploads.s3.amazonaws.com/i/2244qir5zbvg5mbc0zwb.gif)


Cube.js data schema is javascript code and can be easily edited. You can also [dynamically generate schema](https://cube.dev/docs/schema-generation), if needed. 

Let’s update the `schema/Users.js` file. 

We’ll keep only `state`, `id` dimensions, and `count` measure because we’ll need to use them in our example.

```js
cube(`Users`, {
  sql: `SELECT * FROM public.users`,
  dimensions: {
    state: {
      sql: `state`,
      type: `string`
    },
    id: {
      sql: `id`,
      type: `number`,
      primaryKey: true
    }
  }
});
```

That’s it for our backend. We’ve configured the database and created the Cube.js. backend. Now, we’re ready to start working on our frontend application.

## Frontend Dashboard with Highcharts
Let’s generate our app with Cube.js templates. Navigate to the Dashboard App tab and select “Create custom application” with React and Ant Design. 

![Create custom app](https://dev-to-uploads.s3.amazonaws.com/i/4qp7hazs9mhnhflc2rxe.jpg)

It will take some time to create a dashboard app and install dependencies. Once it is finished, you should see the `dashboard-app` folder inside your project’s folder. 

Next, let’s install the dependencies we’ll need. Run the following commands in the `dashboard-app` folder.

```bash
$ cd dashboard-app
$ npm install --save highcharts highcharts-react-official @highcharts/map-collection
```

The command above installs the following dependencies:
- <a href="https://www.npmjs.com/package/highcharts" target="_blank">Highcharts</a>.
- <a href="https://www.npmjs.com/package/highcharts-react-official" target="_blank">Highcharts React wrapper</a>.
- <a href="https://www.npmjs.com/package/@highcharts/map-collection" target="_blank">Highcharts map collection for our map chart wrapper</a>. 

Feel free to remove all the files inside the `src` folder and the `page` folder, as well as update the `dashboard/index.js` file with the following content.

```js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
 <React.StrictMode>
   <App></App>
 </React.StrictMode>,
 document.getElementById('root')
); 
serviceWorker.unregister();
```

Our application will have the following structure:
- `App` as the main app component
- `Dashboard` component that stores data and manages the app state
- `Map`,  `Line`,  `Stock`, and other chart components that manage chart render according to applications data and state.

Let’s create the `<Dashboard />` component in the `dashboard-app/src/components/Dashboard.js` file with the following content. (We’ll create the `<Map />` component later):

```jsx
import React from 'react';
import { Layout } from 'antd';
import { useCubeQuery } from '@cubejs-client/react';
import Map from './Map';
const Dashboard = () => {
 const { resultSet } = useCubeQuery({
   measures: ['Orders.count'],
   dimensions: ['Users.state'],
   timeDimensions: [
     {
       dimension: 'Orders.createdAt',
       dateRange: 'last year',
     },
   ],
 });
if (!resultSet) {
  return “Loading…”;
}
 const data = regions.tablePivot().map(item => [item['Users.state'], parseInt(item['Orders.count'])])
 return (
   <Layout>
     <Map data={data} />
   </Layout>
 );
};
export default Dashboard;
```

In the above snippet, we did several things. We imported [useCubeQuery](https://cube.dev/docs/@cubejs-client-react#use-cube-query) React hook first.
```jsx
import { useCubeQuery } from "@cubejs-client/react";
```
Next, to render the amount of orders in each state, we need to change the data into Highcharts’ format, where the first element is the state key and the second element is the value.

```js
[
    ["us-ca",967],
    ["us-ny",283],
    ["us-wa",239],
    ["us-il",205],
    ["us-tx",190]
]
```

We’re using [resultSet.tablePivot()](https://cube.dev/docs/@cubejs-client-core#result-set-table-pivot) to access data returned from the backend and to prepare it for rendering.

```jsx
const data = regions.tablePivot().map(item => [item['Users.state'], parseInt(item['Orders.count'])])
```

Now, we’re ready to pass our data to the Map chart. Let’s create a new `dashboard-app/src/components/Map.js` file with the following content.

```jsx
import React, { useState, useEffect } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import highchartsMap from 'highcharts/modules/map';
import mapDataIE from '@highcharts/map-collection/countries/us/us-all.geo.json';
highchartsMap(Highcharts);
const staticOptions = {
 chart: {
   styledMode: true,
 },
 credits: {
   enabled: false,
 },
 title: {
   text: 'Orders by region<small>Highcharts Map API</small>',
   useHTML: true,
 },
 colorAxis: {
   min: 0,
 },
 tooltip: {
   headerFormat: '',
   pointFormat: `
     <b>{point.name}</b>: {point.value}`,
 },
 colorAxis: {
   minColor: '#FFEAE4',
   maxColor: '#FF6492',
 },
 series: [
   {
     name: 'Basemap',
     mapData: mapDataIE,
     borderColor: '#FFC3BA',
     borderWidth: 0.5,
     nullColor: '#FFEAE4',
     showInLegend: false,
     allowPointSelect: true,
     dataLabels: {
       enabled: true,
       format: '{point.name}',
       color: '#000',
     },
     states: {
       select: {
         borderColor: '#B5ACFF',
         color: '#7A77FF',
       },
     },
   },
 ],
};
export default ({ data }) => {
 const [options, setOptions] = useState({});
 useEffect(() => {
   setOptions({
     ...staticOptions,
     series: [
       {
         ...staticOptions.series[0],
         data: data,
       },
     ],
   });
 }, [data]);
 return (
   <HighchartsReact
     highcharts={Highcharts}
     constructorType={'mapChart'}
     options={options}
   />
 );
};
```

Inside the `Map.js` file, we imported `useState`, `useEffect` hooks, and a bunch of Highcharts components. Then, we defined chart options based on <a href="https://www.highcharts.com/demo/maps" target="_blank">Highcharts Map API</a> specs.
In `staticOptions`, we can set map styling, source, data, event handlers, and other options.

Highcharts has a wide selection of <a href="https://code.highcharts.com/mapdata/" target="_blank">SVG maps</a> to use. We’ve picked <a href="https://jsfiddle.net/gh/get/jquery/1.11.0/highslide-software/highcharts.com/tree/master/samples/mapdata/countries/us/us-all" target="_blank">this one</a>.

Lastly, we merged our `staticOptions` and `props.data` and then passed it to the Highcharts component.

That’s all for our `<Map/>` component.

Now, we just need to update the ‘dashboard-app/App.js’ to include the `<Dashboard />` component:

```diff
+ import Dashboard from './components/Dashboard';
- <Header />
- <Layout.Content>{children}</Layout.Content>
+ <Dashboard />
```

...and we’re ready to check out our first chart! 

Navigate to [http://localhost:3000](http://localhost:3000) in your browser, and you should be able to see the map chart that we’ve just built.

A similar workflow can be used to create other chart types, as in the GIF below.
![Highcharts React Demo](https://dev-to-uploads.s3.amazonaws.com/i/m58xgktr5g8y4w5agce9.gif)

- Define the static chart options, according to Highcharts API <a href="https://api.highcharts.com/highcharts/" target="_blank">documentation</a>. 
- Add data to options.series.
- Pass options to the Highcharts component.

The full source code of the above dashboard is [available on Github](https://github.com/cube-js/cube.js/tree/master/examples/highcharts), and you can check [the live demo here](https://highcharts.cubecloudapp.dev/#/).

I hope you’ve found this tutorial helpful. If you have any questions or any kind of feedback, please let me know in this [Slack channel](http://slack.cube.dev/).