CSDK Custom Widgets Example [Linux]
Summary: The article by Micael Santana explores the capabilities of the Compose SDK in creating Custom Widgets for embedding applications, particularly with Sisense. It highlights various techniques, such as using the 'useExecuteQuery' method for data retrieval, and leveraging factories for data manipulation. The setup process is detailed, including configurations like API token access and CORS. It demonstrates the creation of custom and replaceable widgets, as well as the integration with the Sisense Dashboard. The article concludes by illustrating the flexibility and possibilities offered by Compose SDK, providing a demo URL and a GitHub repository for code exploration.
Motivation
Compose SDK has a bunch of ways to create Custom Widgets and use them inside your embedding application. This article is intended to show an example of a configurable environment so you can see the possibilities behind Compose SDK features.
Why Create Custom Widgets?
Custom widgets inside CSDK (Compose SDK) can be two different things. We have a feature called Custom Widgets, which is used to replace Plugin widgets inside the DashboardById component, We will discuss that later. The most common case is when you create custom widgets in the framework of your choice, for example:

This is a Top Performers List Widget that doesn’t exist inside Sisense, but nothing blocks us from creating it natively inside React. That is one of the main concepts behind Compose SDK. Using it, you are no longer limited by the Sisense UI, and now you can create any kind of UI that you want.
How to get Data?
To do that, the main method is useExecuteQuery. In React, we call it a hook. Inside it, you can pass the query that you want Sisense to execute.
For example: you want to create a table, you can pass this query to useExecuteQuery:
{
dataSource: DataSource,
dimensions: [Country.Country],
measures: countryTableMeasures(),
filters: filters,
count: TABLE_PAGE_SIZE,
offset: (tablePage - 1) * TABLE_PAGE_SIZE,
};It’s closer to using raw JAQL in the Sisense API, but using the JSON Query Model from CSDK. That allows you to create large queries or complex measures on the Sisense server side.
That is one of the main features of CSDK.
Manipulate the Data
Another important concept is the factories. It has a bunch of them that you can check here. In this demo, we’re mostly using the filterFactory and the measureFactory. The important thing that you should know is that measures and filters are important concepts for creating a query, and the factories help you do that.
In the Top Performers Table, we’re using them, for example:
const quantity = measureFactory.sum(Commerce.Quantity, "Quantity");
const revenue = measureFactory.sum(Commerce.Revenue, "Revenue");
{
dataSource: DataSource,
dimensions: [dimension],
measures: [quantity, revenue],
};You can create several types of measures in the same query, but in this case, we’re using them to get the Quantity and Revenue. This allows Sisense to do these calculations for you, so you don’t need to do the calculations on the client side. The filters follow the same logic, but they are passed in other prop:
In general, you want to pass what is being filtered from your frontend.
The Data Structure
You will see more about that in the setup step. DataModel is the representation of your data model inside Sisense, so if you run the command to get the data model, that will be exactly what your Sisense data model is inside your application. This is essential for creating queries using Type IntelliSense, so you don’t get stuck trying to create queries by hand.
Dynamic Configuration
The core concept of this demo is to allow you to test it in your Sisense deployment. To do that, we use a few concepts from React. What you need to know is the SisenseContextProvider. It is a Context, if you already use React, you probably know what it is, but imagine a context as a pool where you can put some components inside that will consume the same data, and this also applies to Sisense:
export function App() {
const { connection } = useSisenseConnection();
if (!connection) {
return (
<ConnectScreen/>
);
}
return (
<SisenseContextProvider url={connection.url} token={connection.token}>
<Dashboard />
</SisenseContextProvider>
);
}So, using this concept, we can create a preview screen where you can pass any configuration before starting your application. This opens a wide range of implementations using Compose SDK, and this works for any component too. You can use it to display components and connect to Sisense only when it’s needed.
Setup
Creating the Data Model:
To make the setup easier, we use the Data Model that already comes with Sisense. In this demo, we will use the Sample ECommerce data. If you don’t have your ElastiCube running, you must start and publish it. The data that you have inside this ElastiCube is from 2013, so when you apply the filters, keep that in mind:

Creating your Dashboard:
This is only needed if you want to see the demo working with Compose SDK Custom Widgets.
The Plugin Widget that we’re replacing is the Histogram. It’s a free widget from Sisense, so you can apply it to any dashboard. You must have it to see the Custom Widget implementation. The DashboardById component that we’re using doesn’t need to have the Data Model inside the code, so you can use any ID.
You can import this dashboard as example.
After creating you dashboard you should save the id of it, you can find the id in your URL: <YOUR_SISENSE_URL>/app/main/dashboards/<YOUR_DASHBOARD_ID>.
Getting your API Token:
There are a few ways to get your API Token. The simplest one is through the Sisense UI. Another way is by using the Compose SDK CLI. Both methods work. After that, save your token.
In this demo, the token and any other information are not being saved anywhere, so you can safely put your information there.
Configure the CORS:
You must configure your CORS. That is how your Sisense server will recognize this demo as reliable.
To configure it, you must copy the domain/URL from the demo: https://csdk-community-example.vercel.app. You can follow the same steps that are used in the CSDK docs.
Setting the Settings:
Now you can configure the demo. Go to the url, and fill the fields. After you click in connect the dashboard will be opened.

Report Tab
The application is divided into two tabs. The Report Tab contains widgets that were fully created using React without any usage of useExecuteCustomWidgetQuery, and widgets that use it. Those widgets that use the Custom Widget will have a disclaimer at the top. This is where we use most of the measure and filter factories.

The main idea is to show that it is possible to create any kind of widget using it. The widgets here were created using Tailwind, and you can organize the components however you want inside React.
Since your data is coming from useExecuteQuery or useExecuteCustomWidgetQuery, the heavy work will be processed by Sisense.
Some components, like the Indicators, still have some calculations inside the JS, for example:
This is used to get the right size of the filled bar compared to the value. The way that I like to organize it is:
The data that is purely informational, like percentages, revenue, and quantity, I let CSDK handle.
Those kinds of data that are for frontend display, I handle in the frontend.
You need to be careful when doing operations in the frontend. You don’t want to lose your source of truth for the data, which is Sisense.
Filters
Another important part of this dashboard is the filters. Those are applied through React, but they are using the filter factories from Compose SDK. That means those filters are not just on the client side.

In React, you can share context and values between components, like it was explained earlier in this article. So we can pass the values from some widgets, or to all widgets. Using the factory, you can bind the filters to the React state and pass the values to a builder that will apply them to all those widgets, for example:
Custom Widget
In the ReportTab, we use the Custom Widget (Code), which is not related to a WidgetById or DashboardById.

What does that mean? It means that we’re not using outside hooks like useExecuteQuery, and we need to register the widget. The process is almost the same as using it inside a DashboardById or WidgetById component, so you need to register the widget and, right after that, pass the query to the Widget component.
In this example, we’re doing a bunch of style changes, but the idea is the same. Also, we pass the buildReportFilters that we use in other non Custom Widgets.
const { registerCustomWidget } = useCustomWidgets();
registerCustomWidget("topperformers", CustomTopPerformersWidget);
return (
<Widget
key={dim.name}
widgetType="custom"
id={`top-performers-${dim.name.toLowerCase()}`}
title={dim.name}
dataSource={DM.DataSource}
customWidgetType="topperformers"
filters={[
...buildReportFilters(reportOptions),
filterFactory.topRanking(dim, qty, reportOptions.topPerformersCount),
]}
dataOptions={{
dimensions: [{ column: dim }],
value: [
{ column: qty, name: "Quantity" },
{
column: measureFactory.sum(DM.Commerce.Revenue, "Revenue"),
name: "Revenue",
},
],
}}
styleOptions={{
height: 280,
header: { hidden: true },
cornerRadius: "None",
shadow: "None",
spaceAround: "None",
topCount: reportOptions.topPerformersCount,
backgroundColor: "None",
}}
/>
);Using it you can inside the WidgetComponent just get the data instead of fetching the data outside the component inside a Context.
Custom Widget Dashboard

If you do not pass the Dashboard ID, this tab will be disabled.
So, if you pass the Dashboard ID, you will be able to see the DashboardById implementation. This is one of the standard implementations of Compose SDK Custom Widgets Dashboard. One of the main functions of Custom Widgets is to replace Plugin Widgets or Custom Widgets that you created in Sisense.
That means that we need to register or unregister the custom component, like this:
The TopPerformers Widget inside the Dashboard is a different version from the one used in the ReportTab. That is because DashboardById passes the data in a different way, since we’re using a Histogram widget to be replaced.
At the end of the day, the component is the same. What changes is just the data handling that we need to parse to build the widget in the case of DashboardById or WidgetById components.

Conclusion
Compose SDK opens a lot of possibilities when you want to build custom components. In most cases, using Sisense as the source of truth is the right approach, so you avoid having different values. However, in some cases, you will need to change the data shape to fit your component (like in the Custom Widget inside DashboardById). We also showed a way for you to handle the Sisense Context to create custom connections or change configurations dynamically. In addition, we explored a little bit about factories and why you should use them when handling Custom Widgets.
See for yourself and test it in the Demo URL. You can also find the Repository URL on GitHub so you can explore and modify the code.
References/Related Content
Mentioned Plugins: Histogram.
Disclaimer: This is an experimental demo application and not an officially supported Sisense demo. This post outlines a potential custom workaround for specific use cases and provides examples of how Compose SDK features can be implemented. The solution may not work in all scenarios or Sisense versions, so we strongly recommend testing it in your environment before deployment. If you need further assistance with this, please let us know.