Data Studio Custom Visualizations using React

Data Studio Custom Visualizations using React

Google Data Studio is pretty easy to use for simple analytics dashboarding. Its standard visualization components are pretty comprehensive for most of the simple problems I try to solve. When you want to do something more complex, you can build custom visualizations with HTML or SVG using Community Visualizations .

The documentation for writing visualizations is great, but the examples involve direct DOM manipulation:

var chartElement = document.createElement('div');
chartElement.id = 'myViz';
document.body.appendChild(chartElement);

I recently worked on a complex visualization for a test flakiness dashboard, and found it convenient to use React and JSX instead of hand crafting DOM nodes. In this post, I'll develop a simple community visualization that uses React. In a later blog, I'll expand this example to show how it can populate with a datasource from Data Studio.

You can find the code for this example over on brianduff/datastudio-react on GitHub.

Setting up

I'm going to use TypeScript, but you can use plain old JavaScript if you want.

npm init -y

# Install the DSCC library and react
npm install @google/dscc react react-dom
npm install --save-dev @types/react @types/react-dom

# Install and initialize TypeScript (optional)
npm install --save-dev typescript
npx tsc init

Among the things this generates is the tsconfig.json file. We'll make a few minor changes to it. Here's the complete file:

// TODO: more stuff to add here.
{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    // Enable jsx
    "jsx": "react-jsx",
    // Send output to a different folder
    "outDir": "built-tsc"
  },
  // Tell tsc to find our code in a folder called src.
  "include": ["src"]
}

Create the visualization

Let's create a really simple visualization component to get started in src/Hello.tsx:

export function Hello() {
  return <div>Hello!</div>
}

Now, we'll create the entry point that integrates with Data Studio's DSCC library to embed our component in src/index.tsc. There's a bit of unavoidable DOM nastiness here, but it's confined this one place.

import { ObjectFormat, objectTransform, subscribeToData } from '@google/dscc'
import { Hello } from './Hello'
import ReactDOM from 'react-dom'

export function drawViz(data: ObjectFormat) {
  // Insert or replace the visualization element
  let element = document.getElementById('viz')
  if (element) {
    element.parentNode?.removeChild(element)
  }
  element = document.createElement('div')
  element.setAttribute("id", "viz")
  document.body.appendChild(element)

  // Actually render our component
  ReactDOM.render(<Hello />, element)
}

// Connect our drawViz function to Data Studio
subscribeToData(drawViz, { transform: objectTransform })

At this point, we can check that the code compiles using npx tsc. It should emit a built-tsc folder containing JavaScript versions of the above code.

Bundling with webpack

In order to actually deploy the visualization, Data Studio needs it to be bundled up into a self-contained single JavaScript file along with all of its dependencies. For us, this includes the react and jsx runtimes as well as the DSCC library itself. Webpack is designed exactly for this kind of usecase, so we'll use it here.

Let's install webpack:

npm install --save-dev webpack webpack-cli

Then, we'll create a simple webpack config file in webpack.config.js:

const path = require('path')
const isProduction = process.env.NODE_ENV == 'production'

const config = {
  entry: './built-tsc/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'viz.js',
  }
}

module.exports = () => {
  config.mode = isProduction ? "production" : "development"
  return config
}

We can now generate a single dist/viz.js containing the visualization and its dependencies:

npx webpack

Writing the manifest and config

When we deploy custom components for Data Studio, we can actually deploy a set of components in a library. We describe the whole set of components in a manifest file, and each individual component includes a config file. Let's go ahead and create these config files for our component.

manifest.json describes our whole "library" of components. We'll come back to BUCKETNAME and what it means later.

{
  "name": "Example Visualizations",
  "organization": "Brian Duff",
  "description": "Example visualizations using React and JSX",
  "logoUrl": "https://img.icons8.com/dotty/80/000000/view-file.png",
  "packageUrl": "https://duff.blog",
  "supportUrl": "https://duff.blog",
  "components": [
    {
      "id": "hello",
      "name": "Hello",
      "description": "Just says hello",
      "iconUrl": "https://img.icons8.com/material/24/000000/hello.png",
      "resource": {
        "js": "gs://BUCKETNAME/hello/viz.js",
        "config": "gs://BUCKETNAME/hello/hello.config.json"
      }
    }
  ]
}

And hello.config.json describes this simple component we've created. For now, we'll create a config with a simple dimension. We'll customize this in later blog posts.

{
  "data": [
    {
      "id": "data",
      "label": "Data",
      "elements": [
        {
          "id": "someDimension",
          "label": "A Dimension",
          "type": "DIMENSION"
        }
      ]
    }
  ]
}

Deploying to Google Cloud Storage

Data Studio loads custom components from Google Cloud Storage. To deploy our custom component, we must upload the manifest.json, vis.js, and hello.config.json files to a Cloud Storage bucket. Create a new bucket following the instructions in Creating a storage bucket, and make a note of your bucket name. You'll want to modify manifest.json to replace BUCKETNAME with the actual name of the bucket you created.

After that, you can upload using the gsutil command, which you should have installed as part of the bucket creation instructions.

First, you'll want to change the ACLs of the bucket so that they allow public access. New buckets in GCS are created with uniform bucket-level access, which means you can't set permissions for individual files in the bucket (or the bucket itself), and are not visible to public by default. The instructions for Data Studio haven't been updated yet to account for this; the -a public-read option to gsutil cp won't work. Let's go ahead and make our new bucket visible to the public (replace BUCKETNAME with the name of your bucket):

gsutil iam ch allUsers:objectViewer gs://BUCKETNAME

Now, let's organize our files the way we want to upload them:

mkdir -p deploy/hello && \
  cp manifest.json deploy/ && \
  cp dist/viz.js deploy/hello/ && \
  cp hello.config.json deploy/hello/

After that, we can copy the files up to GCS like so:

gsutil cp -r deploy/* gs://BUCKETNAME

Trying it out

We should be able to try the component out in Data Studio now. Go over to datastudio.google.com and create a new blank report. Add a data source to your report (it can just be a Google Sheet). Click on the Community Visualizations and Components toolbar button:

Screen Shot 2021-11-23 at 4.20.07 PM.png

Click "+ Explore More", then click the "Build your own visualization" button in the Community Gallery. In the Manifest path, type the bucket URL of your manifest:

Screen Shot 2021-11-23 at 4.33.23 PM.png

Note here, that you're entering the bucket path (i.e. gs://BUCKETNAME), not the path of the actual manifest.json file within it (e.g. gs://BUCKETNAME/manifest.json). This tripped me up when I first tried this out, and the error message is quite opaque.

Click on the Hello component and grant it permission, and you should see it render in your report:

Screen Shot 2021-11-23 at 4.35.39 PM.png

Scripts to make life easier

Let's make things a bit easier for future development by adding some scripts to package.json. In the scripts section of this file, we'll add the following:

  "scripts": {
    "build": "npx tsc",
    "prepare": "mkdir -p deploy/hello && cp manifest.json deploy/ && cp dist/viz.js deploy/hello/ && cp hello.config.json deploy/hello/",
    "deploy": "npm run build && npm run prepare && gsutil cp -r deploy/* gs://BUCKETNAME"
  },

Now we can build and deploy in one step with npm run deploy.

Wrapping up

Phew, that was a lot for a glorified Hello World :P However, now it's pretty easy to write quite complex custom visualization components using react that work well with Data Studio. In future blogs, I'll explore more of what you can do.

The code for this simple project might serve as a good starting point for your own visualizations. You can grab the code at brianduff/datastudio-react, and let me know in the comments if you run into any issues or have questions!