Build-your-own components
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 {
      // the inputs are passed through to the React component as props
      title: inputs.title,
      body: inputs.body
    };
  }
});

Two things to notice:

  1. inputs tells Embeddable which input fields to show in the UI when using this component. In this case, two text input fields.
  2. props is a function that takes those inputs and maps them onto the props that should be passed into the React component when rendering.

Push to Embeddable

Run npm run embeddable:dev to test out your component, and when you’re happy with it, 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 defineComponent and loadData functions used above are two of the most powerful parts of the Embeddable SDK. You can learn more about them here: defineComponent and loadData.

Adding Component Previews

Embeddable supports real-time previews for all components, including any you create. You will see these previews when you click the "Add Component" button in the dashboard builder.

Image 0

To define your own components, you will need to provide sample data for the various inputs you've defined, especially any required inputs. To easily create sample data, you can use the mock data functions provided by the Embeddable core package.

// src/components/YourComponent/YourComponent.emb.ts
import { mockDataResponse, mockDimension, mockMeasure } from '@embeddable.com/core';

To create sample data, you can use the mockDimension and mockMeasure functions to create mock dimensions and measures, and the mockDataResponse function to create a mock data response. You can keep these definitions in the .emb.ts file or create a separate sample data file to keep things organized.

// src/utils/sampledata.ts
const dimensionName = 'country';
const dimensionGroupName = 'category';
const measureName = 'sales';
 
const sampleData = {
  dimension: mockDimension(dimensionName, 'string', { title: 'Country' });
  dimensionGroup: mockDimension(dimensionGroupName, 'string', { title: 'Category' }),
  measure: mockMeasure(measureName, 'number', { title: 'Sales' }),
  const results1Measure2Dimensions = mockDataResponse(
    [dimensionName, dimensionGroupName, measureName],
    [
      ['US', 'Cat 1', 120],
      ['US', 'Cat 2', 130],
 
      ['GER', 'Cat 1', 100],
      ['GER', 'Cat 2', 110],
 
      ['UK', 'Cat 1', 80],
      ['UK', 'Cat 2', 95],
 
      ['FRA', 'Cat 1', 70],
      ['FRA', 'Cat 2', 60],
 
      ['SPA', 'Cat 1', 55],
      ['SPA', 'Cat 2', 35],
    ],
  );
};

To tell Embeddable you're creating a preview, you can use the definePreview function from the Embeddable React package:

// src/components/YourComponent/YourComponent.emb.ts
import { definePreview } from '@embeddable.com/react';

To define the preview, you can call the definePreview function, passing in your component and an object containing the sample data for each input. Note that required inputs must be provided with sample data for the preview to work correctly. Optional components can be left out if desired, though in some cases they may affect the component preview.

// src/components/YourComponent/YourComponent.emb.ts
export const preview = definePreview(Component, {
  xAxis: sampleData.dimension,
  segment: sampleData.dimensionGroup,
  metric: sampleData.measure,
  results: sampleData.results1Measure2Dimensions,
  title: '',
  showLabels: false,
  enableDownloadAsCSV: false,
  enableDownloadAsPNG: false,
});

Best practice is to put this code below the component meta definition, but above the export default defineComponent(...) line.

Extending Components

You can also extend existing components like the Bar Chart or Table to add custom functionality. This allows you to leverage the existing UI while adding your own logic or data handling.

You can find complete information on extending Remarkable Pro components in our guide to extending existing components