(Update WIP) Add custom components via CLI
You can use Uiflow's live development server and library publisher to register custom React UI and logic components via
@uiflow/cli
npm package. This enables you to add custom components to Uiflow, so you can add more nuanced functionality to your applications.You must complete the following prerequisites to add custom components to Uiflow via CLI:
- Install the
@uiflow/cli
package
To install the
@uiflow/cli
package:- 1.Open a command window.
- 2.Enter the commands below into your command line:mkdir my-custom-componentscd my-custom-componentsnpm init -ynpm install -D typescriptnpm install @uiflow/cli# This sets up your librarynpx uiflow create
After the package is installed, follow the on-screen prompts to set up a Uiflow CLI library.

To stay organized, create a
components
directory in your project and add a sub-directory with an index
entry point to store your custom components. This structure enables you to place component-related assets in their own directory.To create your component directory:
- 1.Open a command window.
- 2.Enter the commands below into your command line:mkdir -p components/MyUIComponentcd components/MyUIComponenttouch index.tsx
Once you complete the steps above, your new directory path should appear as:
<rootDir>/components/MyComponent/index.tsx
To make your custom component accessible in Uiflow during development, you must register it in the component code. To register your components:
- 1.Open the
index.tsx
file you created in the Create a directory for custom components section in a text/code editor. - 2.Code your custom component in the
index.tsx
file. If you already have the code for your custom component, copy and paste it into the file. - 3.Save your
index.tsx
file.
You can register two types of components via CLI:
This example shows how to register a custom Simple Button UI component:
import * as React from 'react';
import { PropType, registerComponent } from '@uiflow/cli';
export type Props = React.HTMLProps<HTMLButtonElement> & {
label: string;
};
const SimpleButton: React.FC<Props> = ({ className, onClick, label }) => {
return (
<button className={className} onClick={onClick}>
{label}
</button>
);
}
export default registerComponent('my-ui-component', {
component: SimpleButton,
name: 'Simple Button',
props: [
{
name: 'label',
type: PropType.String,
value: 'My Label',
},
{
name: 'onClick',
type: PropType.Event,
},
],
});
The following table describes a few major properties of custom UI components:
Name | Type | Description |
---|---|---|
name | String | Name of the property. |
type | call | event | boolean | number | string | object | image | video | color | boolean[] | number[] | string[] | object[] | Type of property. |
label | String | Custom property label. If missing, the label appears as name . |
descriptions | String | Description of the property's purpose and use. |
options | Array<type> | When the type is string , you can pass an array of options to display them as a select . |
arguments | Array<type> | When the type is event , you can pass an array of other properties to define the arguments in your event .Note: arguments cannot be a type of event. |
onEmit | Function | When the type is event , you can pass a custom emit logic. For example:onEmit({ args, emit }) { emit('onClick', { prop1: args[0] }); } |
Custom logic components are comprised of different blocks containing various logic. This example shows how to register a custom
sum
logic component, which you can use to add two numbers together:import { PropType, registerComponent } from '@uiflow/cli';
export default registerComponent('my-logic-component', {
name: 'Sum',
blocks: [
{
input: {
type: PropType.Call,
name: 'sum',
arguments: [
{
name: 'value1',
type: PropType.Number,
value: 0,
},
{
name: 'value2',
type: PropType.Number,
value: 0,
},
],
onEmit({ inputs, emit }) {
const result = inputs.value1 + inputs.value2;
emit('onResult', { result });
},
},
output: {
type: PropType.Event,
name: 'onResult',
arguments: [
{
name: 'result',
type: PropType.Number,
},
]
}
},
]
});
There are five block types you can add to logic components:
- Action block
- Action block with async callback
- Getter block
- Transform block
- Event block
For samples of each block type, see the sections below.
Blocks included in your logic component appear on your Uiflow component according to this schema:

Sample action block
Action blocks enable your component to update properties and emit events.
<strong> input: {
</strong> type: PropType.Call,
name: "callName",
arguments: [
{
name: 'input1',
type: PropType.Number,
value: 123,
},
{
name: 'input2',
type: PropType.Number,
},
],
// Here you can update your props and emit events
onEmit({ setProps, inputs, emit }) {
setProps({ prop1: inputs.input1, prop2: inputs.input2 });
emit('onResult', { result: 'my result' });
},
},
}
Sample action block with async callback
Action blocks with async callbacks enable your component to update properties and emit events.
{
input: {/* same as above */}
output: {
type: PropType.Event,
name: 'onResult',
arguments: [
{
name: 'result',
type: PropType.String,
},
]
}
}
Sample getter block
Getter blocks enable your component to emit property values.
{
output: {
type: PropType.Number,
name: 'getMyValue',
onEmit({ props }) {
return props.myValue;
}
}
}
Sample transform block
Transform blocks enable your component to emit the combined value of multiple inputs.
{
input: [
{
name: 'input1',
type: PropType.Number,
},
{
name: 'input2',
type: PropType.Number,
},
],
output: {
type: PropType.Number,
name: 'myResult',
onEmit({ inputs }) {
return inputs.input1 + inputs.input2;
}
}
}
Sample event block
Event blocks enable your component to emit events.
{
output: {
type: PropType.Event,
name: 'onCallback',
arguments: [
{
name: 'result',
type: PropType.Number,
},
],
}
}
The following table outlines a few major component configuration options you can code into your custom components:
Name | Type | Description |
---|---|---|
name | String | Name of the component. |
description | String | Markdown | Description of the property's purpose and use. |
icon | String | Icon of the component. You can pass SVG code as a string to display the icon in the Uiflow component catalog. |
component | React.ComponentType | React component you want to render. |
render | Function | Custom component you want render. This option is an alternative to component . |
blocks | Array<BlockType> | Component blocks you can use in the Logic view. |
props | Array<PropertyType> | Properties of the component. |
styleProps | Array<PropertyType> | Style properties of the component. |
order | Number | Where the component appears in the library category. The highest number is the latest component. |
hasPlaceholder | Function | This method validates your UI components in Uiflow and returns a string (only in Uiflow) when the case conditions are not met (e.g., invalid props, no children ). For example:hasPlaceholder({ props }) { if (!props.label) { return “Label can’t be empty”; } } |
deprecated | Boolean | Declare the component as deprecated and hide it from the component catalog. |
Now that you have registered and configured your component, you can test it in Uiflow Studio by running
npm start
in your command line. This opens a Uiflow development app in your web browser where you can drag-and-drop your registered components into the app via the Design view or Logic view.
The
component
option enables you to pass declared props
to your component. If you need more control (e.g. you need to emit an internal state from your component), you can use the render
option.This example shows how to extend the Simple Button component to display a counter node in Uiflow that tracks how many times the button was clicked:
import * as React from 'react';
import { PropType, registerComponent } from '@uiflow/cli';
// ...
export default registerComponent('my-ui-component', {
name: 'Simple Button',
props: [
// ...
{
name: 'onClick',
type: PropType.Event,
arguments: [
{
name: 'counter',
type: PropType.Number,
}
],
onEmit({ args, emit }) {
// The `newCounter` is passed as the first argument of `props.onClick`
const newCounter = args[0];
// Specify the event name and arguments to emit
emit('onClick', { counter: newCounter });
}
},
],
render({ props, className }) {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
const newCounter = counter + 1;
setCounter(newCounter);
props.onClick(newCounter);
}
return (
<SimpleButton
className={className}
label={props.label}
onClick={onClick}
/>
)
}
});
An application that uses your component is inherently dependent on that component’s definition (e.g. the component’s properties). When you create a new version of your component, you may cause compatibility issues if you change parts of the definition on which the application relies (e.g., you rename or delete a property, change its type, etc.). To avoid compatibility issues with your new component version, try to create a backward-compatible update.
The following example shows a successful backward-compatible update for a custom component:
// Initial component
export default registerComponent('my-component', {
name: 'My Component',
props: [
{
name: 'label',
type: PropType.String,
}
],
render({ props }) {
return (
<div>{props.label}</div>
)
}
});
// Updated component
export default registerComponent('my-component', {
name: 'My Component',
props: [
{
name: 'label',
type: PropType.String,
},
// Added new property
{
name: 'content',
type: PropType.String,
},
],
render({ props }) {
// In previous versions, the `props.content` will be undefined
return (
<div>{props.label}</div>
<div>{props.content || 'No content'}</div>
)
}
});
When backward-compatibility is not possible, deprecate the old version and start a new one from scratch. After you deprecate the old version, it is no longer available in the catalog. However, keeping it in your library enables existing components in your app to continue functioning properly.
The example below demonstrates how to deprecate your old component and build a new one from scratch:
// Deprecate the old component
// components/MyComponent/index.ts
export default registerComponent('my-component', {
// Deprecate the component
deprecated: true,
name: 'My Component',
props: [
{
name: 'label',
type: PropType.String,
}
],
render({ props }) {
return (
<div>{props.label}</div>
)
}
});
// Create new component version from scratch
// components/MyComponentV2/index.ts
export default registerComponent('my-component-v2', {
// You can keep the same name or change it in something like "My Component V2"
name: 'My Component',
props: [
{
name: 'content',
type: PropType.String,
}
],
render({ props }) {
return (
<div>{props.content}</div>
)
}
});
Once you feel satisfied with your component, you can publish it to your Uiflow library. This makes the component available in the Libraries panel for everyone in your organization.
To publish your component:
- 1.Open a command window.
- 2.Enter
npm run publish
into the command line. - 3.Follow the on-page instructions.

Last modified 5d ago