Interactivity in Embeddable
By combining events and variables, you can build interactive components that let users change filters, choose granularities, or pick values that dynamically affect other components or data sources.
- No-Code Flexibility: Non-technical teammates can build dashboards, hooking up interactive components in a no-code interface.
- Reuse & Scale: One dropdown or date picker can drive multiple charts just by referencing the same variable.
- Modular Logic: You decide how your component fires events (e.g.
onChange
,onSelect
), and Embeddable handles mapping those to variables or other actions in the builder.
How It Works
Define Variables
- In the no-code builder, you can create variables like
granularity
orselectedValue
. - You can also pre-define variables in your
.emb.ts
file (viameta.variables
) to make life easier for non-technical users. The examples below demonstrate how to do this.
Fire Events
- Your React component emits events (e.g.
onChange
) whenever the user interacts with a dropdown, textbox, or other control. - For instance, a dropdown might fire
onChange: { value: 'United States' }
.
Update Variables
- Events can be mapped back to variables, updating their value inside Embeddable.
- For instance, when a dropdown fires
onChange
, it might update a variable namedselectedCountry
.
Propagate Changes
- In the no-code builder, you can connect variables to other components (or the same one) and datasets, essentially using variables as filters or inputs, causing data to reload or the UI to update.
- For example, a chart referencing
selectedCountry
will re-fetch data whenever the variable changes.
Examples
A Basic Dropdown
Below is an .emb.ts
file for a dropdown component that loads a list of country values from a dataset. It leverages events and variables to provide an interactive experience.
import { defineComponent, EmbeddedComponentMeta, Inputs } from '@embeddable.com/react';
import { loadData, Value } from '@embeddable.com/core';
import Component from './index';
export const meta: EmbeddedComponentMeta = {
name: 'BasicDropdownComponent',
label: 'Basic Dropdown',
defaultWidth: 320,
defaultHeight: 80,
inputs: [
{
name: 'ds',
type: 'dataset',
label: 'Dataset to display',
},
{
name: 'values',
type: 'dimension',
label: 'Values',
config: {
dataset: 'ds',
},
},
{
name: 'defaultValue',
type: 'string',
label: 'Default value',
description: 'Initial value',
},
],
events: [
{
name: 'onChange', // Pass an event called OnChange to your component as a prop
label: 'Change', // How this event appears in the builder UI when defining interactions
properties: [
{
name: 'value', // The property name to be passed back to the builder
type: 'string', // The value's expected type
array: false // Set to true for a multi-select dropdown
},
],
},
],
variables: [
{
name: 'chosen value', // Variable created automatically when this component is added
type: 'string',
defaultValue: Value.noFilter(), // Initial variable value (this can also be set in the no-code builder)
inputs: ['defaultValue'], // Connects the variable to the 'defaultValue' input, which is passed into the React component
events: [{ name: 'onChange', property: 'value' }], // On the 'onChange' event, update the 'chosen value' variable with the 'value' property from the event
},
],
};
export default defineComponent<Inputs>(Component, meta, {
props: (inputs) => {
return {
...inputs,
// Load dimension values for the dropdown
results: loadData({
from: inputs.ds,
dimensions: [inputs.values],
}),
};
},
events: {
// Maps the 'onChange' event from your component to the event described in meta
onChange: (value) => ({ value: value || Value.noFilter() }),
},
});
Usage Flow
- Add Component: A teammate drags the BasicDropdownComponent onto the canvas in the builder, choosing a dataset and picking a dimension.
- Auto-Created Variable: A variable named
chosenValue
is automatically added to the builder’s variable list and connected to thedefaultValue
input. - Data Loading: The chosen dimension’s values are fetched and passed to the React component.
defaultValue
is also passed to the component - this may be empty or have an initial value (e.g. 'United States'), depending on what's been defined in the no-code builder. - Interactivity: When an end-user selects an option from the dropdown,
onChange
fires, updatingchosenValue
. Other components (or filters) referencingchosenValue
immediately respond - for instance, filtering a dataset by that value.
The result is a reusable dropdown that non-technical users can drop into dashboards. The dropdown logic remains local to your React component, while the no-code builder manages hooking up the selected value to one or multiple charts or datasets.
Example: Granularity Picker
Below is an .emb
file for a granularity picker component that lets users choose day, week, or month.
// src/components/GranularityPicker/GranularityPicker.emb.ts
import { defineComponent, EmbeddedComponentMeta, Inputs } from '@embeddable.com/react';
import { Value } from '@embeddable.com/core';
import GranularityPickerComponent from './index';
export const meta: EmbeddedComponentMeta = {
name: 'GranularityPicker',
label: 'Granularity Picker',
inputs: [
{
name: 'defaultGranularity',
type: 'string',
label: 'Default Granularity',
description: 'Initial granularity (e.g. "day", "week", or "month")'
}
],
events: [
{
name: 'onPickGranularity',
label: 'Pick Granularity',
properties: [
{
name: 'value',
type: 'granularity'
}
]
}
],
variables: [
{
name: 'chosenGranularity',
type: 'granularity',
defaultValue: 'month', // or Value.noFilter() if you want no default
inputs: ['defaultGranularity'], // Ties the 'defaultGranularity' input to this variable
events: [ // Ties this variable to the onPickGranularity
{
name: 'onPickGranularity',
property: 'value'
}
]
}
]
};
export default defineComponent(GranularityPickerComponent, meta, {
props: (inputs: Inputs<typeof meta>) => {
return {
...inputs
};
},
events: {
// Maps the 'onPickGranularity' event from your component to the event described in meta
onPickGranularity: (value) => ({ value: value || Value.noFilter() })
}
});
Usage Flow
- Add Component: A teammate drags the Granularity Picker onto the canvas.
- Auto-Created Variable: Embeddable auto-creates a variable called
chosenGranularity
in the no-code builder, connecting this to thedefaultGranularity
input. If no initial value is specified in the builder, then the default value ismonth
, as defined in the code. - User Interacts: The user clicks one of the Day/Week/Month buttons defined in the component, at which point,
onPickGranularity(value)
fires, sending an event to Embeddable and updating thechosenGranularity
variable accordingly. - Other Components React: Any component (or filter) referencing
chosenGranularity
will now automatically reload or update, applying the new granularity (e.g. timeDimensions in a line chart).
The result is a reusable granularity picker that non-technical users can drop into dashboards. The picking logic remains local to your React component, while the no-code builder manages hooking up that granularity to one or multiple charts.
Pie Chart Example
Below is a minimal pie chart configuration. When the user clicks on a pie segment, it triggers an onClick
event and sends Embeddable the clicked metric
and dimension
.
// src/components/PieChart/PieChart.emb.ts
import { Value, loadData } from '@embeddable.com/core';
import { EmbeddedComponentMeta, Inputs, defineComponent } from '@embeddable.com/react';
import PieChartComponent from './index';
export const meta: EmbeddedComponentMeta = {
name: 'PieChart',
label: 'Pie Chart',
category: 'Charts',
inputs: [
{
name: 'ds',
type: 'dataset',
label: 'Dataset to display',
},
{
name: 'slice',
type: 'dimension',
label: 'Slice',
config: { dataset: 'ds' },
},
{
name: 'metric',
type: 'measure',
label: 'Metric',
config: { dataset: 'ds' },
}
],
events: [
{
name: 'onClick', // Prop name in the React component
label: 'Click', // The event label in the builder
properties: [
{ name: 'slice', type: 'string' },
{ name: 'metric', type: 'number' }
]
}
]
};
export default defineComponent(PieChartComponent, meta, {
props: (inputs: Inputs<typeof meta>) => {
return {
...inputs,
results: loadData({
from: inputs.ds,
dimensions: [inputs.slice],
measures: [inputs.metric],
})
};
},
events: {
// If a slice is clicked, send back 'slice' and 'metric' values or 'noFilter'
onClick: (value) => ({
slice: value.slice || Value.noFilter(),
metric: value.metric || Value.noFilter(),
})
}
});
Usage flow
-
Add Component: A teammate drags the PieChart onto the canvas in the no-code builder.
-
Data Loading: Based on the selected dataset, dimension, and measure,
loadData
fetches the necessary rows. These rows become available underprops.results.data
in your React component. -
Rendering: The Pie Chart component receives
props.results.data
, along with any other inputs (title
,showLegend
). It contains the logic to render the pie chart (perhaps using a charting library like ChartJS or HighCharts). -
Variable creation: No variables are configured in the code, so the user needs to configure one manually in the builder, or two to capture both the dimension and metric. They might call these
clickedDimension
andclickedMetric
. -
Interactivity: When a user clicks on a slice, the React component calls
props.onClick({ slice, metric })
. Embeddable translates this into an event described in the.emb.ts
file, updating the created variable in the builder with the dimension name (e.g."Europe"
) and the measure (e.g.12345
). -
Dashboard Updates: Connected components or datasets react to the update. For instance, a table component whose underlying dataset is being filtered by the
clickedDimension
variable immediately filters rows to show only"Europe"
sales.
This flow shows how no-code configuration meets full-code logic, letting non-technical users pick the data and handle events while you focus on rendering and behavior in the React component.
Clearing Values
If your component needs to “reset” a chosen value - like clearing a text field or returning a dropdown to “no selection” - you can use Value.noFilter()
. This tells Embeddable that any associated variable should be emptied i.e. it no longer has a specific value.
import { Value } from '@embeddable.com/core';
export default defineComponent(MyComponent, meta, {
events: {
onChange: (value) => {
// If 'value' is empty or null, revert to noFilter
return { value: value || Value.noFilter() };
}
}
});