Creating your own React design system analytics tool
Posted 13 November 2024
Services like Omlet offer a way to understand how your React design system is being used across projects. This is very useful for a team maintaining a design system. But the pricing structure isn't the cheapest and scales per React component. I looked into a way of rolling our own system that could give us some similar insights without the cost.
react-scanner
react-scanner is an npm package that statically analyzes your React application. It can output JSON describing how components from a given package are being used across the codebase.
Using @typescript-eslint/typescript-estree and astray the package will construct and traverse an abstract syntax tree (AST) of your codebase. It finds imports of components from your design system, as well as which props are used.
Setting up a CLI
I wrapped react-scanner
in a CLI that I could execute in our projects' CI pipeline on each release:
#! /usr/bin/env node
import { Command } from "@commander-js/extra-typings";
const program = new Command()
.option("-d, --dir <path>", "set project directory");
program.parse();
const options = program.opts();
const dir = path.resolve(options.dir) || cwd();
const scannerConfig = {
rootDir: dir,
crawlFrom: "./src",
includeSubComponents: true,
importedFrom: "your-design-system-package",
processors: [
["raw-report", { outputTo: `${dir}/raw-report.json` }],
],
};
await scanner.run(scannerConfig);
You can find the structure of this in the react-scanner documentation.
I also wanted to collect some extra data about the project that I'd write to a separate project info JSON file:
const pkg = JSON.parse(fs.readFileSync(`${dir}/package.json`, "utf8"));
// Create project info file
const projectInfo = {
name: pkg.name,
pkgVersion: pkg.dependencies["your-design-system-package"],
timestamp: new Date().getTime(),
url: process.env.CI_PROJECT_URL,
};
if (!projectInfo.pkgVersion) {
console.log("No design system package version found in package.json, exiting...");
process.exit();
}
- name - The name of the project to identify where the report came from
- pkgVersion - The version of the design system package used in the project, to understand how up to date each project is
- timestamp - The time the report was generated, to understand how up to date the report is and to help with viewing change over time
- url - The URL of the project so we can construct links to go directly to the file a component is used. We use GitLab and this env variable is set in GitLab CI pipelines.
Storing and viewing the data
I decided to go with a simple static site to display the data, like the approach I used for DORA metrics.
I set up a GitLab project access token for the project and used the GitLab commits API to push data to the main
branch of the data repository. This would trigger a new release of the site, with some data processing of the raw report and project info JSON files.
We use a shared CI template for our frontend projects, so I was able to add a post-deployment step to scan and push the data from other team's projects.
How it's going
Now we have a single place to get a quick view of how our shared components are used across projects. It's useful for both engineers and designers to reason about how changing behaviour might affect projects.
I'm interested in exploring how we can use this information to guide our design system strategy. Metrics of design system adoption over time can help us understand how we can better support our teams. Being able to see when teams start or stop using a new component gives us an opportunity to reach out and understand why.