Custom Elements
Learn how to create custom elements in the builder.
Builder vs Vueform Elements
If you’re familiar with Vueform as a form-rendering engine, you already know the different element types such as text
, textarea
, date
, signature
, and more. In the builder, these Vueform elements are used as the foundation to create builder elements. A builder element includes a schema
property (explained in more detail later) that defines the Vueform element type and its default configuration options, like label
, description
, and others.
Multiple builder elements can utilize the same Vueform element type with varying default options. For instance, different builder elements such as Text Input, Number Input, Email Address, Password, and URL all rely on the Vueform text
element type. However, each is customized through specific properties. For example, the Number Input element has inputType: 'number'
and rules: ['nullable', 'numeric']
, which turns it into a 'Number Input' element.
Creating Custom Builder Elements
When creating a custom builder element, there are typically two approaches:
Using Existing Vueform Element Types: Leverage existing Vueform elements like
text
ortextarea
and extend them to create new builder elements with custom configurations.Creating New Vueform Element Types: Develop an entirely new Vueform element type and integrate it within the builder.
The following sections will explain both scenarios in detail.
Element Definition
Elements can be registered in builder.config.js
under element.types
:
// builder.config.js
import { defineConfig } from '@vueform/builder'
export default defineConfig({
element: {
types: {
myType: {
label: 'My element',
description: 'My awesome element',
icon: 'https://domain.com/my-element-icon.svg',
category: 'static',
schema: {
type: 'my-type',
},
rules: [ /* ... */ ],
operators: [ /* ... */ ],
sections: { /* ... */},
separators: { /* ... */ },
},
// ...
}
}
})
Name | Type | Description |
---|---|---|
[key] | string | The object key of the element type (myType in the example). |
label* | string | The label of the element in the element list. |
description* | string | The description of the element in the element list. |
icon | string | The url of the element's icon in the element list (if left empty a default icon will be displayed). It's recommended to use SVG to keep the UI high quality on each display. |
category | string | Under which category should the element appear in the element list (if using categories). |
schema* | object | The schema of the element that will be added. |
rules | array | The list of validation rules the element config panel should have if it has validation. |
operators | string | Defines the list of operators that should be available in the dropdown list when the element is targeted with conditions. If left empty is empty and is not empty options will appear. |
sections* | string|object | This determines what configuration options the element has when selected. It can be a string with the name of an other element which's sections should be reused (eg 'text' ) or an object containing custom configuration options (more about it later). |
separators* | string|object | Defines the separators between of configuration options (more about it later). |
Overriding Existing Element Types
All builder elements that you can find on the left panel by default are stored in elementTypes
export of @vueform/builder
:
import { elementTypes } from '@vueform/builder'
console.log(elementTypes)
/**
* Logs:
*
* captcha: { label: '...', description: '...', icon: [...], ... },
* checkbox: { label: '...', description: '...', icon: [...], ... },
* checkboxBlocks: { label: '...', description: '...', icon: [...], ... },
* checkboxTabs: { label: '...', description: '...', icon: [...], ... },
* checkboxgroup: { label: '...', description: '...', icon: [...], ... },
* ....
* verticalSlider: { label: '...', description: '...', icon: [...], ... },
*/
Elements define a schema
property, an object that contains all the default options for the element (like type
, label
, description
, etc.). Here's how schema
looks like for select
type:
import { elementTypes } from '@vueform/builder'
console.log('select: ', elementTypes.select)
/**
* Logs:
*
* select: {
* label: 'Select',
* description: 'Select input',
* icon: ['fas', 'caret-square-down'],
* category: 'fields',
* schema: {
* type: 'select',
* items: [
* { value: 0, label: 'Label' },
* ],
* search: true,
* native: false,
* label: 'select_field_label',
* inputType: 'search',
* autocomplete: 'off',
* }
* }
*/
If we want to customize it, eg. change the default items
we can do so by overriding element.types.select.schema.items
in builder.config.js
:
// builder.config.js
import { defineConfig, elementTypes } from '@vueform/builder'
export default defineConfig({
element: {
types: {
select: {
...elementTypes.select,
schema: {
...elementTypes.select.schema,
items: [
{ value: 'apply', label: 'Apple' },
{ value: 'orange', label: 'Orange' },
{ value: 'pear', label: 'Pear' },
]
}
}
}
}
})
If we have a localized builder we can take this a step further and provide translation tags instead of item labels as they are automatically resolved from the current locale:
// builder.config.js
import {
defineConfig,
elementTypes,
en_US,
nl_NL,
} from '@vueform/builder'
en_US.select_default_apple = 'Apple'
en_US.select_default_orange = 'Orange'
en_US.select_default_pear = 'Pear'
nl_NL.select_default_apple = 'Appel'
nl_NL.select_default_orange = 'Sinaasappel'
nl_NL.select_default_pear = 'Peer'
export default defineConfig({
element: {
types: {
select: {
...elementTypes.select,
schema: {
...elementTypes.select.schema,
items: [
{ value: 'apply', label: 'select_default_apple' },
{ value: 'orange', label: 'select_default_orange' },
{ value: 'pear', label: 'select_default_pear' },
]
}
}
}
},
builderLocales: {
en_US,
nl_NL,
},
})
We can use the same method to override any property of an element including label
, description
, icon
, category
or even sections
and separators
.
In the next chapter we will learn about Custom Config Panels that will reveal how we can customize
sections
andseparators
.
Reusing Existing Element Types
We can not only override existing elements, but use the elementTypes
to add new elements similar to an existing element.
Let's say we want to use the select
element type and create a User selector element type, that automatically fetches all the available users from our server:
// builder.config.js
import { defineConfig, elementTypes } from '@vueform/builder'
export default defineConfig({
element: {
types: {
user: {
...elementTypes.select,
label: 'User selector',
description: 'Select a user from the database',
icon: 'https://domain.com/user-element-icon.svg',
category: 'fields',
schema: {
type: 'select',
label: 'Users',
native: false,
search: true,
resolveOnLoad: false,
minChars: 2,
delay: 300,
inputType: 'search',
autocomplete: 'off',
items: '/api/users',
},
},
},
},
})
We've got an element that is identical to the original select
type only that its properties and default schema
are customized.
Now the only thing left is to add this new user
element to the list of available elements. Continue reading to learn how.
Registering Elements & Display Order
To make an element available/unavailable in the builder or change the order of existing elements we can override the elements
property in builder.config.js
.
The full list of available elements is available here. We can use this list to create a customized list of elements we want to make available. Here's a customized list with our user
element type included after number
:
// builder.config.js
import { defineConfig } from '@vueform/builder'
export default defineConfig({
element: {
types: {
user: {
// ... `user` type from previous example
}
}
},
elements: [
'text',
'number',
'user', // <-- "user" type added here so it will be displayed after "Number input"
'location',
'textarea',
// ...
]
})
Customizing Config Panel
Please head to Custom Config Panels chapter to learn how to customize the config panel of your new element.
Disabling Element Features
Element features such as editing or removing can be disabled by adding a builder
object to the element schema:
// builder.config.js
import { defineConfig } from '@vueform/builder'
export default defineConfig({
element: {
types: {
user: {
// ...
schema: {
type: 'select',
label: 'Users',
// ...
builder: {
remove: false, // Disables removing the element
clone: false, // Disables cloning the element
move: false, // Disables moving the element
edit: false, // Disables editing the element
resize: false, // Disables resizing the element
}
},
},
}
}
})
Containers and lists containing an element with disabled features will also have their features disabled. This is true for each from above except for
edit
.
Defining Condition Operators
We can define the list of operators that should be available when the element is targeted in the condition selector modal.
// ...
export default {
element: {
types: {
user: {
// ...
schema: {
type: 'select',
label: 'Users',
// ...
},
operators: [
'is empty',
'is not empty',
'is equal to',
'is not equal to',
]
},
},
},
},
The full list of operators available:
is empty
is not empty
is equal to
is not equal to
> than
>= than
< than
<= than
starts with
ends with
contains
Operators might be provided as objects, eg:
{ value: 'is equal to', label: 'is any equal to' }
{ value: 'is not equal to', label: 'is not equal to any' }
Creating a New Element Type
If we want to add a custom element, which is not based on an existing Vueform element (like text
, textarea
, select
, etc.), first we have to create it and register it for Vueform based on the docs in Vueform's Creating Elements section.
Let's create a new element called LogoElement
(type logo
) which only job is to display a logo in either black & white or colored format:
<!-- LogoElement.vue -->
<template>
<ElementLayout>
<template #element>
<img
src="https://vueform.com/images/logo.svg"
alt="Vueform Logo"
title="Vueform Logo"
width="45"
height="39"
:class="classes.img"
/>
</template>
<!-- Default element slots -->
<template v-for="(component, slot) in elementSlots" #[slot]><slot :name="slot" :el$="el$"><component :is="component" :el$="el$"/></slot></template>
</ElementLayout>
</template>
<script>
import { ref } from 'vue'
import { defineElement } from '@vueform/vueform'
export default defineElement({
name: 'LogoElement',
props: {
color: {
type: String,
default: 'bw',
required: false,
}
},
setup(props, { element }) {
const defaultClasses = ref({
img: '',
img_bw: 'grayscale',
$img: (classes, { color }) => ([
classes.img,
color === 'bw' ? classes.img_bw : null
])
})
return {
defaultClasses,
}
}
})
</script>
<style>
/* If we were to define CSS we can put it here */
</style>
Let's register it in vueform.config.js
so it can be used as type: 'logo'
:
// vueform.config.js
import { defineConfig } from '@vueform/builder'
import LogoElement from './LogoElement.vue'
export default defineConfig({
// ...
elements: [
LogoElement,
],
})
Usage:
<template>
<Vueform>
<LogoElement name="my_logo" ... />
</Vueform>
<!-- or -->
<Vueform :schema="{
my_logo: {
type: 'logo'
}
}" />
</template>
Now, we can add it to the builder:
// builder.config.js
import { defineConfig } from '@vueform/builder'
export default defineConfig({
// Registering the `logo` element type
element: {
types: {
logo: {
label: 'Logo',
description: 'Company logo',
icon: 'https://domain.com/logo-element-icon.svg',
category: 'static',
rules: [],
schema: {
type: 'logo', // <- using our new `LogoElement` type
},
sections: {
// ...
},
separators: {
// ...
},
}
}
},
// Adding `logo` to the list of available elements
elements: [
// ...
'logo',
// ...
]
})
As you can see we have now created a custom Vueform element and added it to the builder. When the element is dragged to the form it will appear with the default configuration defined in schema
(which in our case only contains type
).
The next logical step is to create a custom configuration panel for the element, as it likely requires a unique configuration that cannot be handled by the existing panels for other elements.
To learn how to do it, head to the next chapter: Custom Config Panels.