Add a Custom Node Type
Custom nodes let you extend Workflow Builder with node types that match your specific domain and business logic.
Overview
Section titled “Overview”Each node type consists of four files inside apps/frontend/src/app/data/nodes/<node-name>/:
| File | Purpose |
|---|---|
schema.ts | JSON Schema defining the node’s properties |
uischema.ts | UI Schema controlling how properties render |
default-properties-data.ts | Default values for all properties |
<node-name>.ts | Node definition (label, type, icon) exported to the palette |
Step 1: Define the node
Section titled “Step 1: Define the node”Create apps/frontend/src/app/data/nodes/my-node/my-node.ts:
import { PaletteItem } from '@workflow-builder/types/common';
import { defaultPropertiesData } from './default-properties-data';import { MyNodeSchema, schema } from './schema';import { uischema } from './uischema';
export const myNode: PaletteItem<MyNodeSchema> = { label: 'My Node', description: 'Does something useful', type: 'my-node', icon: 'Star', defaultPropertiesData, schema, uischema,};Step 2: Write the JSON Schema
Section titled “Step 2: Write the JSON Schema”Create schema.ts to define what properties the node has:
import { NodeSchema } from '@workflow-builder/types/node-schema';
export const schema = { properties: { label: { type: 'string' }, description: { type: 'string' }, targetService: { type: 'string', options: [ { label: 'Email', value: 'email' }, { label: 'Slack', value: 'slack' }, { label: 'Webhook', value: 'webhook' }, ], }, },} satisfies NodeSchema;
export type MyNodeSchema = typeof schema;Step 3: Write the UI Schema
Section titled “Step 3: Write the UI Schema”Create uischema.ts to control layout and field rendering:
export const uischema = { type: 'VerticalLayout', elements: [ { type: 'Control', scope: '#/properties/label' }, { type: 'Control', scope: '#/properties/description' }, { type: 'Control', scope: '#/properties/targetService', options: { format: 'dropdown' }, }, ],};Step 4: Set default values
Section titled “Step 4: Set default values”Create default-properties-data.ts:
export const defaultPropertiesData = { label: 'My Node', description: '', targetService: 'email',};Step 5: Register the node
Section titled “Step 5: Register the node”Add the new node to the palette in apps/frontend/src/app/data/palette.ts:
import { myNode } from './nodes/my-node/my-node';
const getPaletteDataFunction = (): PaletteItemOrGroup[] => { return [ triggerNode, action, // ...existing nodes myNode, // add here ];};The new node type now appears in the palette and can be dragged onto the canvas.
Conditional fields
Section titled “Conditional fields”Use rules in the UI Schema to show or hide fields based on other field values:
{ type: 'Control', scope: '#/properties/webhookUrl', rule: { effect: 'SHOW', condition: { scope: '#/properties/targetService', schema: { const: 'webhook' }, }, },}This renders webhookUrl only when targetService is 'webhook'.
Further reading
Section titled “Further reading”The properties form is rendered by JSONForms. The schema.ts and uischema.ts files follow the JSONForms JSON Schema and UI Schema specifications. Refer to the JSONForms docs for the full list of layouts, controls, rules, and renderers.
See also: Node Library overview