Links

Custom component

Create your own components to customize your data as you wish
You can create a custom component to add functionality that is not present in the UI Bakery components list.

Basics

As a developer, you can create a custom component to use in UI Bakery. This component can have its own logic and interface that is defined by you. Additionally, the custom component can communicate with other features in UI Bakery by triggering events and receiving data to display.
Custom components can be written in pure JavaScript or can import custom libraries, such as jQuery or React.
Please note that the custom component is rendered inside of an iframe, thus we recommend using it only for the fix-sized elements and avoid overlays and popups inside of it.
Additionally, you can use the Unrestricted custom component to render any HTML and JavaScript without any restrictions. This component is not rendered inside of an iframe.

Component anatomy

A Custom component is an HTML page embedded within an iframe that can contain any HTML, CSS, and JavaScript. You can provide its code in the Code setting. The Custom component can communicate with other UI Bakery components, actions, and app state. It can receive data and fire events.
A typical custom component would have the following structure:
  • 3rd party scripts
  • custom styles
  • root element (usually a <div> tag)
  • custom JavaScript script tag
<!-- 3rd party scripts and styles -->
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
<!-- custom styles -->
<style>
body {
padding: 1rem;
}
p {
margin-top: 0;
}
button {
margin-bottom: 1rem;
}
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
<!-- root element where the component will be rendered -->
<div id="root"></div>
<!-- custom logic -->
<script type="text/babel">
function CustomComponent() {
// receive data from UI Bakery
const data = UB.useData();
return (
<div className="container">
<p>Data from UI Bakery: {data.title}</p>
<button onClick={() => UB.triggerEvent("Data from custom component")}>Trigger Event</button>
<input onChange={(event) => UB.updateValue(event.target.value)} placeholder="Set state"></input>
</div>
);
}
const Component = UB.connectReactComponent(CustomComponent);
ReactDOM.render(<Component />, document.getElementById("root"));
</script>
By default, the custom component is rendered inside an iframe, so there are no specific limitations to the code and styles specified by the developer.

Passing data inside of the component

To pass the data into your custom component use the "Data" setting. This can be done by passing a JavaScript object that contains the necessary data, such as:
{
data: [1,2,3],
display: 'only_new',
}
Additionally, you can pass the data using JS API in your actions:
ui.customComponent.setData({ ... })
To access this data within the custom component, use the following code:
const data = UB.useData()
You can also subscribe to updates of the data using the following code:
UB.onData(data => {
console.log('new data', data);
});

Receiving data and triggering actions from the component

If your custom component produces events or needs to trigger an action, use the following code:
  • To set the value of the component, use the following code inside the custom component:
UB.updateValue('Data from custom component');
Once executed, the new value is available as {{ui.customComponent.value}};
  • To trigger an action, use the following code inside the custom component:
UB.triggerEvent('Data from custom component');
Then, subscribe your action to the On Event trigger of the custom component. Once the UB.triggerEvent('data') is executed, the assigned action will be triggered.
The data supplied to the triggerEvent() function is available as {{ui.customComponent.value}} as well as {{params}} variable in the assigned action.

jQuery example

Copy and paste the whole example into the custom component Code field:
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<style>
body {
padding: 1rem;
}
p { margin-top: 0 }
button { margin-bottom: 1rem }
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
<div class="container">
<p>Data from UI Bakery: <span id="uibakeryData"></span></p>
<button id="triggerEvent">Trigger Event</button>
<input id="updateValue" placeholder="Set state"></input>
</div>
<script>
$('#triggerEvent').click(() => UB.triggerEvent('Data from custom component'));
$('#updateValue').change(event => UB.updateValue(event.target.value));
UB.onData(({ title }) => {
$('#uibakeryData').text(title);
});
</script>

React example

Copy and paste the whole example into the custom component Code field:
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
<div id="root"></div>
<style>
body {
padding: 1rem;
}
p { margin-top: 0 }
button { margin-bottom: 1rem }
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
<script type="text/babel">
function CustomComponent() {
const data = UB.useData();
return (
<div className="container">
<p>Data from UI Bakery: {data.title}</p>
<button onClick={() => UB.triggerEvent('Data from custom component')}>Trigger Event</button>
<input onChange={event => UB.updateValue(event.target.value)} placeholder="Set state"></input>
</div>
);
}
const Component = UB.connectReactComponent(CustomComponent);
ReactDOM.render(<Component />, document.getElementById('root'));
</script>

Custom Calendar example

In this example, we will create a custom calendar to display the appointments.
  1. 1.
    Start with loading the data: add an action - Load Table with appointments data.
  2. 2.
    Find a Custom component and place it onto the working area.
  3. 3.
    In the component's Data field, assign the newly created action:{ events: {{ actions.loadAppointments.data }} }.
4. In the Code field, type in the below code:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/main.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/main.min.js"></script>
<div class="container">
<div id="calendar"></div>
</div>
<style>
body, html {
height: 100%;
padding: 0;
margin: 0;
}
.container {
background: white;
padding: 2rem;
height: 100%;
overflow: hidden;
border-radius: 0.25rem;
border: 0.0625rem solid #dde1eb;
box-shadow: 0 0.5rem 1rem 0 rgb(44 51 73 / 10%);
}
.fc-daygrid-event-harness {
cursor: pointer;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
eventClick: (info) => {
// Update UI variable value
UB.updateValue({ id: info.event.id });
// Event triggering
UB.triggerEvent({ id: info.event.id });
}
});
calendar.render();
// Callback to process new data in custom component
UB.onData(data => {
calendar.removeAllEvents();
const events = data && data.events ? data.events : [];
if (events && events[0]) {
// In case of new data, the first event is automatically selected
UB.updateValue({ id: events[0].id });
UB.triggerEvent({ id: events[0].id });
}
events.forEach(event => {
calendar.addEvent(event);
});
});
});
</script>
Your calendar is ready now!

Google Charts example

Google Charts offer a vast variety of charts, so you can connect the charts library to UI Bakery and code your own chart.
Now, let's create a project milestones Gantt chart. We will use the data about each project milestone from the data source and show its timeline and details on a Gantt chart.
Custom Gantt chart
  1. 1.
    Load your data and add a Table to display it.
For this example, we are using Google Sheets as a data source. In the table, there are the following columns:
  • Task
  • Team
  • Responsible
  • Start date
  • ETA
  • Percent done.
2. Create a new Action - Code step. This action is to map the data to later use it in the chat configuration:
const rawRows = await {{actions.loadPhases.trigger()}};
const msInDay = 24 * 60 * 60 * 1000;
return rawRows.map((row) => [
// Task ID
row.row_number_id.toString(),
// Task name
row.Task,
// Task's start date
new Date(row['Start date']),
// Task's end date can be omitted (null), if you have duration
null,
// Duration in milliseconds. Use null if you have an end date
row.ETA*msInDay,
// % of completion
row['Percent done'] * 100,
// List dependencies, if necessary. Need to be listed comma-separated, e.g.'Development,NDA'
null,
]);
3. Find a Custom Component and drag it onto the working area.
4. In the Data field of the component, assign the newly created action: {{actions.mapData.data}}.
5. In the component's Code field, you'll need to specify the code for the Gantt. We'll take the sample from the documentation and then make adjustments according to your data:
<html>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<head>
// Variables initialization, that will be used in a chart
let isLoaded = false;
let rows = [];
let chart;
let chartData;
// Loading Gantt chart library
google.charts.load('current', {'packages':['gantt']});
// Chart initialization only after it's loaded
google.charts.setOnLoadCallback(() => {
// Chatt initialization
chart = new google.visualization.Gantt(document.getElementById('chart_div'));
chartData = new google.visualization.DataTable();
// Setting up chart structure
chartData.addColumn('string', 'Task Id');
chartData.addColumn('string', 'Task Name');
chartData.addColumn('date', 'Start Date');
chartData.addColumn('date', 'End Date');
chartData.addColumn('number', 'Duration');
chartData.addColumn('number', 'Percent Complete');
chartData.addColumn('string', 'Dependencies');
isLoaded = true;
drawChart();
});
// Callback to process new data in custom component
UB.onData((data) => {
rows = data ?? [];
// The data can be received before the chart is initialiased.
// That's why rendering of a chart with new data should be done after chart initialization
if (isLoaded) {
drawChart();
}
});
function drawChart() {
chartData.addRows(rows);
const options = {
height: 600,
gantt: {
trackHeight: 30
}
};
chart.draw(chartData, options);
}
</script>
</head>
<body>
<div id="chart_div"></div>
</body>
</html>
Check the component now - you will see a Gantt chart populated. When you hover over a milestone, you will see additional details about it.

MUI React library template example

Here's an example of how to connect and use the MUI library to build custom components in UI Bakery:
Resulting component
Copy and paste the following code into the custom component Code setting:
<!-- React -->
<script src="https://unpkg.com/[email protected]/umd/react.development.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js" crossorigin="anonymous"></script>
<!-- MUI -->
<script src="https://unpkg.com/@mui/[email protected]/umd/material-ui.development.js" crossorigin="anonymous"></script>
<!-- Fonts to support Material Design -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/>
<!-- Icons to support Material Design -->
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<div id="root"></div>
<script type="text/babel">
const {
Avatar,
Button,
CssBaseline,
TextField,
FormControlLabel,
Checkbox,
Link,
Grid,
Box,
Typography,
Container,
createTheme,
ThemeProvider
} = MaterialUI;
const theme = createTheme();
function Copyright(props) {
return (
<Typography variant="body2" color="text.secondary" align="center" {...props}>
{'Copyright © '}
<Link color="inherit" href="https://mui.com/">
Your Website
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
function App() {
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
console.log({
email: data.get('email'),
password: data.get('password'),
});
};
return (
<ThemeProvider theme={theme}>
<Container component="main" maxWidth="xs">
<CssBaseline />
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}/>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Box component="form" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>
<TextField
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
/>
<TextField
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Sign In
</Button>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</Box>
</Box>
<Copyright sx={{ mt: 8, mb: 4 }} />
</Container>
</ThemeProvider>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App/>);
</script>