Adding Code

JS and JSX code

Code for Insights are specified in both a JS and JSX section. Code can be entered directly when you create or edit an Insight on Stormly.

When an Insight runs it first evaluates the js code and will be mostly used for logic, calculations and data retrieval. After that the jsx is evaluated and used to display the visualizations and results, mostly using ready visualizations and helpers the Stormly API provides.

A return statement at the end of the js code can return any object, which will be available in the jsx code under the global variable data. Note that there cannot be any commented code after the return statement.

Most code generally looks something like this:

JS code:
const a = 1;
const b = 2;
const helloItems = [<Txt>Text A</Txt>, <Txt>Text B</Txt>];
const sumFunc = (a, b) => {
return a + b;
return {a: a, b: b, helloItems: helloItems, sumFunc: sumFunc};
JSX code:
<Txt>The sum of {data.a} + {data.b} is: {data.sumFunc(data.a, data.b)}</Txt>

It will output the following on the Insight results page:

The sum of 1 + 2 is: 3
Text A
Text B

The spread operator is currently not supported. A workaround is to use Object.assign, so instead of:

const obj1 = {a: 1, b: 2};
const obj2 = {b: 3, c: 4};
const obj3 = {...obj1, ...obj2}; // obj3 is now {a: 1, b: 3, c: 4}

Use this:

const obj3 = Object.assign({}, obj1, obj2); // obj3 is now {a: 1, b: 3, c: 4}

Global variables

All global variables listed below are available in both the js and jsx code.

  • data — Contains whatever is returned from the js code. This variable is an exception in that it is only available in the jsx code.
  • questions — an object with all answers to Template and Insight questions as answered by end-users. For more details see Adding Variables.
  • standalone — if this Insight is currently being run on top of a report or plugin this will be false. Useful if the same Insight can be both stand-alone and non-standalone.
  • now — a JS Date object with the date set to the time when the Insight was first run. Because Insights have an archive history this can be a date from the past, or the current date if a new run. Useful where you need to access to the current date, such as current-time sensitive API helper functions that check if there was enough data for the last X days.
  • development — When an Insight is created with the check [✅] Run in development mode, this variable will be true. Useful for example to show debugging information when in development mode, or re-use cached results without having to wait.
For non-standalone Insights the following global variables are available:
  • results — the current ReportData object if this Insight is run on top of a report, or the PluginData when run on top of plugin results. Useful for example if you want to make a different visualization of a specific report or plugin.
  • report — the current Report object if this Insight is run on top of a report. Useful for example if you want to make a forecast of any report with a timeseries, or create an Insight that automatically segments any Report based on the clusters of users found.

Displaying errors

When a string or error object is returned in the JS code, it will be picked up and displayed as an error to the end-user.

Returning an error string:
if (questions.age < 18) {
return "You need to be at least 18 years old to use this Insight!";

Or we can return an error object with more details. All fields are optional except for "code": "error":

Returning an error object:
if (questions.age < 18) {
return {
"code": "error",
"title": "You are not old enough",
"explanation": "You need to be at least 18 years old to use this Insight!",
"backtrace": " invalid_age"

This will be displayed to the end-user of the Insight as:

Insight error - Collapsed Insight error - Expanded

As explained later in the chapter on working with data and plugins, the ReportData.get and PluginData.get return a detailed error object in case they have an error. The error object can be returned as-is:

Returning a report error as-is:
const reportData = await ReportData.get(new Report({sql: "SELECT nothing"}));
if (!Utils.validResult(reportData)) {
return reportData;
Returning a plugin error as-is:
const pluginData = await PluginData.get("plugins/forecast", {
data: reportObjectWithTooLittleData
if (!Utils.validResult(pluginData)) {
return pluginData;

Checking data availability

Often you want to check before you start running your Insight if there is enough data at all, such as: is there data for the last 30 days, with a minimum of 20 users per day?
The Utils.amountOfDataFor helper is what you need. Note that this helper's first argument is the now object — this makes sure that when this Insight is being opened from the Archive it checks using the historical date range.

The example below checks if there have been at least 20 users per day, for the last 30 days:

const minimumDaysOfDataNeeded = 30;
const minimumUsersPerDay = 20;
const dataCheck = await Utils.amountOfDataFor(
if (!dataCheck.hasEnoughData) {
return `You need a minimum of ${minimumUsersPerDay} users per day for the last ${minimumDaysOfDataNeeded} days.`;

The dataCheck object also contains information on how when the first and last data was seen in that period, how many days ago was the first data seen and more.
In addition, Utils.hasEnoughDataFor can be used with the same arguments and returns a boolean directly.

For more details and arguments, such as supplying different report objects and working with custom date ranges, see amountOfDataFor in the api-library.

Helpers and components

More helper and visualization components can be found in the Helpers and Components api-library documentation.