Development
Defining components

Defining Components

You can create custom React components in Embeddable using any JS libraries you like.

Key capabilities include:

  • Define Inputs: Make re-usable components by defining inputs your team can customize in the builder.
  • Load Data: Dynamically fetch data from your database via Embeddable's loadData function.
  • Pass Events: Capture user actions (e.g. button clicks) and send them back to Embeddable for interactive features like filtering.

How It Works

Build Your React Component (.tsx)

Write a standard React component - handling your UI, props, and business logic.

For example, this simple text component displays a title and body:

// src/components/TextComponent/index.tsx
 
import React from 'react';
 
type Props = {
  title: string;
  body: string;
};
 
export default ({ title, body }: Props) => {
  return (
    <div>
      <h1>{title}</h1>
      <p>{body}</p>
    </div>
  );
};

There's nothing Embeddable-specific about this code - it's just standard React.

Configure in .emb.ts

In a companion .emb.ts file, define inputs and other configuration, including how to load data. This file tells Embeddable how to expose the component in the no-code builder so it can be configured by your team without code:

// src/components/TextComponent/TextComponent.emb.ts
 
import { EmbeddedComponentMeta, defineComponent } from '@embeddable.com/react';
import Component from './index';
import { Inputs } from '@embeddable.com/react';
 
export const meta = {
  name: 'TextComponent', // a unique identifier that must match the file name (i.e. TextComponent.emb.ts)
  label: 'Text component', // user-facing name in the builder
  inputs: [
    {
      name: 'title', // unique identifier for this input
      type: 'string',  // determines the UI control to render (i.e. a text field in this case)
      label: 'Title', // the label shown in the builder
    },
    {
      name: 'body', // unique identifier for this input
      type: 'string', // renders a second text field. 
      label: 'Body', // the label shown in the builder
    }
  ]
} as const satisfies EmbeddedComponentMeta;
 
//The function that tells the SDK to include this component in the no-code builder.
export default defineComponent(Component, meta, {
  props: (inputs: Inputs<typeof meta>) => {
    return {
      ...inputs // the inputs are passed through to the component as props
    };
  }
});

Push to Embeddable

When you’re done, push your component code to Embeddable. Your teammates can now drag, drop, and configure it in their dashboards - no extra coding required.

Image 0Image 1

Example: a simple data component

Below is a simple KPI chart component that loads data from your database:

// src/components/KPIChart/index.tsx
 
import React from 'react';
import { DataResponse, Measure } from '@embeddable.com/core';
import Loading from '../util/Loading'
import Error from '../util/Error'
 
type Props = {
  title?: string;
  metric?: Measure; // { name, title }
  results: DataResponse; // { isLoading, error, data: [{ <name>: <value>, ... }]
};
 
export default ({ title, metric, results }: Props) => {
 
  const { isLoading, data, error } = results;
 
  if(isLoading) {
    return <Loading />
  }
 
  if(error) {
    return <Error msg={error}/>;
  }
 
  const value = results.data?.[0]?.[metric.name] ?? 'No data';
 
  return (
    <div>
      {title && <h1>{title}</h1>}
      <p>{value}</p>
    </div>
  );
};

And its .emb file, defining the inputs and configuration:

// src/components/KPIChart/KPIChart.emb.ts
 
import { EmbeddedComponentMeta, defineComponent, Inputs } from '@embeddable.com/react';
import { loadData } from '@embeddable.com/core';
import Component from './index';
 
export const meta = {
  name: 'KPIChart', // an identifier - must match KPIChart.emb.ts
  label: 'KPI Chart', // user-facing name in the builder 
  inputs: [
    {
      name: 'title',
      type: 'string',
      label: 'Title text',
    },
    {
      name: 'ds',
      type: 'dataset', // shows a dropdown of available datasets. These are created directly in the Builder.
      label: 'Dataset',
    },
    {
      name: 'metric',
      type: 'measure', // shows a dropdown of measures (defined in your data models)
      label: 'KPI',
      array: false, // specifies that users can select a single measure 
      config: {
        dataset: 'ds', // restricts measure options to the selected dataset
      }
    },
  ]
} as const satisfies EmbeddedComponentMeta;
 
//The function that tells the SDK to include this component in the no-code builder.
export default defineComponent(Component, meta, {
  props: (inputs: Inputs<typeof meta>) => {
    return {
      ...inputs, // the inputs are passed through to the component as props
      results: loadData({ // fetches data from your database and passes it to your component
        from: inputs.ds,
        measures: [inputs.metric], 
      })
    };
  }
});

The loadData function used above is one of the most powerful parts of the Embeddable SDK. You can learn more about it here.

The defineComponent Function

The defineComponent function, used inside your emb.ts files, is how you register and configure components in Embeddable.

export default defineComponent(Component, meta, {
  props: (inputs: Inputs<typeof meta>) => {
    return {
      ...inputs, // the inputs are passed through to the component as props
      results: loadData({ ... }), // fetches data from your database and passes it to your component
    };
  },
});

Parameters

When you call defineComponent in your .emb.ts file, you pass in three arguments:

ParamTypeRequired
componentComponent✅ Yes
metaMeta✅ Yes
configConfig✅ Yes
  • component: your React component.
  • meta: an object defining how the component appears and which inputs are available in the builder.
  • config: an object specifying how user inputs map to props, how local state is managed, and how events are handled.

1. component

component is simply your React component:

import Component from './index';

2. meta

meta is the second argument to defineComponent. It governs how a component is displayed and configured in the builder, including the inputs it needs.

  import { EmbeddedComponentMeta } from '@embeddable.com/react';
 
  export const meta = {
    name: 'MyComponent', 
    label: 'My Component', 
    category: 'Charts',
    classNames: ['container'],
    defaultWidth: 200,
    defaultHeight: 100,
    inputs: [...],
    events: [...],
    variables [...],
  } as const satisfies EmbeddedComponentMeta;
ParamTypeRequired
namestring✅ Yes
labelstring✅ Yes
classNamesstring[]❌ No
defaultWidthnumber❌ No
defaultHeightnumber❌ No
inputsInputMeta[]❌ No
eventsEventMeta[]❌ No
variablesVariableMeta[]❌ No
  • name: A unique identifier that must match the .emb.ts filename. Changing it later can break references.
  • label: A user-facing name shown in the builder.
  • classNames: CSS class names for styling the container.
  • inputs: Input fields users configure (e.g. strings, numbers, datasets, as well as custom types).
  • defaultWidth / defaultHeight: Initial size in the builder (in pixels, snapped to a grid).
  • events: Specifies events the component can trigger (e.g. onChange, onClick), which is key to interactivity. Learn more about events here.
  • variables: Auto-creates variables in the no-code Builder when the component is used, which are key to interactivity. Learn more about variables here.

InputMeta Definition

export const meta: EmbeddedComponentMeta = {
    name: 'BasicDropdownComponent',
    label: 'Basic Dropdown',
    inputs: [
        {
            name: 'ds',
            type: 'dataset',
            label: 'Dataset',
            description: 'Dataset',
            category: 'Dropdown values'
        },
        {
            name: 'property',
            type: 'dimension',
            label: 'Property',
            config: {
              dataset: 'ds',
              supportedTypes: ['time'] // Only let user pick time dimensions
            },
            category: 'Dropdown values'
        },
        ...
    ]
}
ParamTypeRequired
namestring✅ Yes
typeCustomType or NativeType✅ Yes
labelstring✅ Yes
descriptionstring❌ No
defaultValueany❌ No
configobject❌ No
arrayboolean❌ No
categorystring❌ No
  • name: A unique identifier for this input (within the component).
  • type: Determines the UI control (e.g., string, dimension, or custom). Native types are described below.
  • label: User-facing label in the builder.
  • description: A helpful reference, not currently displayed in the builder.
  • defaultValue: The initial value for the input.
  • category: Assigns an optional grouping for this input in the builder UI. Inputs sharing the same category appear together.
  • config: An arbitrary JS object that is type-specific. The config object is passed to the no-code editor to enable custom behavior. A common use-case includes specifying the dataset for dimension or measure inputs and limiting the options that display based on their type.
  • array: Allows users in the builder to select or add multiple values. Applies to inputs of type dimension, measure, and string.

Native Input Types

When defining inputs in your .emb.ts file, you can select from a set of native types that Embeddable supports by default. Each type dictates which UI control appears in the no-code builder and how the data is passed to your component.

  • string renders a text input in the builder.
  • number renders a numeric input in the builder.
  • boolean renders a dropdown (true/false) in the builder.
  • time represents a single point in time, either absolute or relative.
    // Absolute date
    const t1: Time = { date: new Date(1980, 3, 28) }
     
    // Relative date
    const t2: Time = { relativeTimeString: 'last week' }
  • timeRange represents a range between two time points, either absolute or relative.
    // Absolute from / to
    const tr1: TimeRange = { from: new Date(1995, 1, 16), to: new Date() }
     
    // Relative
    const tr2: TimeRange = { relativeTimeString: 'last 30 days' }
  • dataset lets users pick a dataset in the builder.
  • measure represents a measure (count, sum, etc.), as defined in your data models.
  • dimension represents a dimension (e.g. group by country or category), as defined in your data models.
  • granularity for grouping time dimensions in loadData.
    const g: Granularity = 'day'
    // or 'week', 'month', 'year', etc.
  • dimensionOrMeasure represents an input that can be either a dimension or a measure, letting the user choose in the builder.

If you need more specialized behavior than these native types provide (e.g., custom date pickers or advanced styling), you can define custom types along with a custom editor. These built-in types, however, cover most standard use cases for text, numbers, booleans, time inputs, and data references.

EventMeta Definition

Events are used to create interactive components. For example, a dropdown containing a list of countries that, when clicked, filters the data. Or a set of buttons showing 'day', 'week', and 'month' that, when clicked, update the x-axis granularity on a time-series chart.

export const meta: EmbeddedComponentMeta = {
    name: 'BasicDropdownComponent',
    label: 'Basic Dropdown',
    inputs: [],
    events: [
        {
            name: 'onChange',
            label: 'Change',
            properties: [
                { name: 'value', type: 'string', label: 'Clicked Dimension' },
            ],
        },
    ]
}
ParamTypeRequired
namestring✅ Yes
labelstring✅ Yes
propertiesEventProperty[]❌ No
  • name: The event handler name passed into your React component. E.g. setting it to onBigClick means that Embeddable will inject a function into your React component, via its props, called onBigClick. When called, the value you pass to this function will be passed to events.onBigClick inside defineComponent where you need to map the value onto an object that has the shape described in events.properties in your meta. This allows Embeddable to make your event available to use as an interaction in the no-code builder.

  • label: How this event is labeled in the builder.

  • properties: Describes data passed to Embeddable when the event fires.

    EventProperty Definition

ParamTypeRequired
namestring✅ Yes
typeCustomType or NativeType✅ Yes
labelstring❌ No
arrayboolean❌ No
  • name: The property key in your events mapping (e.g. value).
  • type: The expected data type (e.g. string, number, boolean).
  • label: An optional label for this property in the builder.
  • array: Allows multiple values if set to true.

VariableMeta Definition

Variables are a core-concept in Embeddable. They are used mainly to store the values passed to Embeddable from events fired by interactive components.

Variables can be created manually in the no-code builder or they can be defined in code and then created automatically. Defining them in code is preferable because it contributes to a faster, more intuitive experience within the builder - especially for non-technical users.

export const meta: EmbeddedComponentMeta = {
    name: 'BasicDropdownComponent',
    label: 'Basic Dropdown',
    inputs: [],
    events: [],
    variables: [
        {
            name: 'chosen value',
            type: 'string',
            defaultValue: Value.noFilter(),
            inputs: ['defaultValue'],
            events: [{ name: 'onChange', property: 'value' }],
        },
    ]
}
ParamTypeRequired
namestring✅ Yes
typeCustomType or NativeType✅ Yes
defaultValueany❌ No
inputsstring[]❌ No
eventsEventRef[]❌ No
  • name: The variable’s name when auto-created in the builder.

  • type: The variable type (string, boolean, etc.).

  • defaultValue: The initial value for the variable.

  • inputs: A list of inputs whose value should be linked to the variable. The names must match the name of an input defined in inputs.name in your meta.

  • events: If you want certain events to update this variable’s value.

    EventRef Definition

    ParamTypeRequired
    namestring✅ Yes
    propertystring✅ Yes
    • name: References an event from meta.events.
    • property: References a property in that event (e.g., { name: 'onChange', property: 'value' }).

3. config

config is the third argument to defineComponent. It controls what's passed to your component as props and how local .emb state (Embeddable State) is managed.

ParamTypeRequired
props(inputs: Inputs<typeof meta>, [state, setState], clientContext) => any✅ Yes
events{ [eventName: string]: (payload: any) => any }❌ No
  • props(inputs, [state, setState], clientContext)
    • inputs: Values entered by the user in the no-code builder.

    • state and setState are state local to the .emb file, enabling you to pass state from your react component back to the .emb. file. The most common use-case is to load data dynamically from within the component.

    • clientContext: Any additional data passed in from the web component at embed time (e.g. theming, environment info).

      export default defineComponent(MyComponent, meta, {
        props: (inputs, [state, setState], clientContext) => {
          return {
            ...inputs,
            clientContext, // e.g. { theme: 'dark', locale: 'en-US' }
          };
        }
      });
  • events describes how events your component triggers are transformed into a data object that Embeddable can use for interactivity, as described in events.properties in meta.

A Detailed Example

// src/components/Buttons/Buttons.emb.ts
 
import { Value } from '@embeddable.com/core';
import { EmbeddedComponentMeta, Inputs, defineComponent } from '@embeddable.com/react';
 
import Component from './index';
 
export const meta = {
  name: 'Buttons', // Must match the .emb.ts filename; unique ID for this component
  label: 'Button List', // User-facing name in the builder UI
  defaultWidth: 400, // Initial width in pixels when dropped on the canvas
  defaultHeight: 80, // Initial height in pixels when dropped on the canvas
  category: 'Controls: buttons', // Optional grouping/category in the builder
  
  inputs: [
    {
      name: 'values',
      type: 'string',
      array: true, // Allows multiple string values (multi-select)
      label: 'Values',
      category: 'Button values' // Groups these inputs in the builder UI
    },
    {
      name: 'title',
      type: 'string',
      label: 'Title',
      category: 'Settings' // Another input category for organizational purposes
    },
    {
      name: 'selectedValues',
      type: 'string',
      array: true,
      label: 'Default value',
      category: 'Pre-configured variables'
    }
  ],
 
  events: [
    {
      name: 'onChange', // Event name your React component expects 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
          type: 'string',
          array: true // Indicates multiple string values can be returned
        }
      ]
    }
  ],
 
  variables: [
    {
      name: 'chosen value', // Variable created automatically when this component is added
      type: 'string',
      array: true,
      defaultValue: Value.noFilter(), // Initial variable value (if none provided)
      inputs: ['selectedValues'], // Connects the variable to the 'selectedValues' input
      events: [{ name: 'onChange', property: 'value' }] // On the 'onChange' event, update the 'chosen value' variable with the 'value' property from the event
    }
  ]
} as const satisfies EmbeddedComponentMeta;
 
export default defineComponent(Component, meta, {
  props: (inputs: Inputs<typeof meta>, [state], clientContext) => {
    // Maps the inputs defined in meta to the props of your React component
    return {
      ...inputs,
      clientContext // e.g. { theme: 'dark', locale: 'en-US' }
    };
  },
  events: {
    // Maps the 'onChange' event from your React component to an event described in meta
    onChange: (v) => ({ value: v || Value.noFilter() })
  }
});