FormKit's schema is a JSON-serializable data format for storing DOM structures and component implementations, including FormKit forms. Although created specifically for implementing and generating forms, the format is capable of generating any HTML markup or using any third-party components.
Schemas are rendered using FormKit's <FormKitSchema>
component, which is not registered globally by default. You will need to import it:
import { FormKitSchema } from '@formkit/vue'
FormKit ships with first-class support for generating forms using schema. This makes it possible to store generated forms in databases, files, or even QR codes! To generate a form, pass your schema array to the <FormKitSchema>
component using the :schema
prop.
<FormKitSchema :schema="yourSchemaArray" />
Let’s look at a quick example:
We see many features above including the $el
and $cmp
props, the $formkit
prop shorthand, validation, conditional rendering, labels, help text, and multiple types of inputs. We'll unpack all these features in the remainder of this page.
A schema is an array of objects or strings (called "schema nodes"), where each array item defines a single schema node. There are 3 types of of schema nodes:
$el
property.$cmp
property.$formkit
property. Syntactic sugar for the full $cmp
format.Schemas support advanced features like conditional logic, boolean operators, loops, slots, and data scoping — all guaranteed to serialize to a string.
HTML elements are defined using the $el
property. You can use $el
to render any HTML element. Attributes can be added with the attrs
property, and content is assigned with the children
property:
Notice in the above example that the style
attribute is unique in that it should be defined as an object of style to value pairs rather than a string.
Components can be defined with the $cmp
property. The $cmp
property should be a string that references a globally defined component or a component passed
into FormKitSchema
with the library
prop:
In order to pass concrete components via the library
prop, it's best to wrap your library with Vue’s markRaw
signature.
In addition to the schema array (and optional library), the FormKitSchema
object can also include a data
prop. Values from the data object can then be referenced directly in your schema — and your schema will maintain the reactivity of the original data object.
To reference a value from the data object, you simply use a dollar sign $
followed by the property name from the data object. References can be used in attrs
, props
, conditionals and as children
:
Notice in the above example that we used an array to concatenate "Hello" and "$location". We did this because data references and logical expressions in the schema must always begin with a dollar sign $
— otherwise they are treated as unparsed string literals.
Schemas support calling functions that are in your original reference data — and you can even pass data references as arguments of that function!
Just like JavaScript — you can access properties of a deeply nested object using dot-syntax object.property
:
Schema references can have any structure or properties, but at the root of the data reference object there are 2 reserved words: $slots
and $get
.
Schemas also support logic in the form of boolean logic, comparison, and arithmetic expressions. These expressions can be used anywhere a data reference can be used (attrs
, props
, conditionals, and children
):
Expressions must always begin with a $
. If the first element of an expression is a data reference (ex: $count + 2
), then it already begins with a $
and no further labeling is required. However, often the first character of an expression is not a dollar sign — these expressions need to be "labeled" with $:
— for example $: ($count * 3) - 7
.
Although it looks very much like JavaScript — schema expressions are not JavaScript. They are better thought of as a templating language. Expressions are compiled down to functional JavaScript at setup
but the syntax is not 1-1 compatible with JavaScript. This improves performance and provides a critical layer of security as only explicitly exposed data and functionality can be executed.
Schema expressions are limited to the following operators and parenthesis:
Operator | Use case |
---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Modulus |
&& | Boolean AND |
|| | Boolean OR |
=== | Strict equals |
!== | Strict not equals |
== | Loose equals |
!= | Loose not equals |
>= | Greater than or equal |
<= | Less than or equal |
> | Greater than |
< | Less than |
FormKit schema can leverage references and expressions to make schema nodes and attributes conditional. These conditionals can be added in two ways:
if
property on $el
and $cmp
nodes.if/then/else
objectif
propertyBoth $el
and $cmp
schema nodes can leverage an if
property that roughly equates to a v-if
in Vue. If the expression assigned to the if
property is truthy, the node is rendered, otherwise it is not:
Conditional or iterative (when using if
or for
) schema nodes should always include an explicit key
prop. Without this prop Vue may reuse the DOM nodes from the previous render, which can lead to unexpected behavior, errors, and performance issues.
if/then/else
objectThe if/then/else
object allows for more complex conditional logic. It can be used to conditionally render nodes, a list of schema nodes, values of the attrs
object or values of the props
object. It is also possible to nest if/then/else
objects to create more complex structures — similar to an else if
statement in JavaScript.
if/then/else
on schema nodesYou can use the if/then/else
object anywhere you would normally use a schema node. This includes the root schema array, or the children
property of another schema node:
Conditional or iterative (when using if
or for
) schema nodes should always include an explicit key
prop. Without this prop Vue may reuse the DOM nodes from the previous render, which can lead to unexpected behavior, errors, and performance issues.
if/then/else
on attrs and propsYou can also use if/then/else
statements to conditionally output the values of attrs
or props
:
Both $el
and $cmp
schema nodes support looping. The loop syntax is similar to v-for
in Vue and expects an object or array to iterate over and a property to assign the current iteration value to. Optionally, you can also capture the index or property of the current iteration:
Conditional or iterative (when using if
or for
) schema nodes should always include an explicit key
prop. Without this prop Vue may reuse the DOM nodes from the previous render, which can lead to unexpected behavior, errors, and performance issues.
Schemas can render the slot content of the <FormKitSchema>
component anywhere within the schema that a normal schema node can be rendered. All scoped slots are automatically provided to the schema under the $slots
reference object:
Inside of a $formkit
schema node, it is also possible to pass content to preexisting FormKit slots like label
or prefix
inside of the node's __raw__sectionsSchema
property. Read more about raw values below, and sectionsSchema
in the inputs documentation.
At times it may be necessary to pass an object of variable or unknown attributes or props to a $cmp
or $el
. In Vue we would do this using v-bind
— in schema land we use the bind
property:
At times it may be necessary to prevent a given attribute or prop from being parsed. This can be done by prefixing an attribute or prop with __raw__
:
{
$cmp: 'PriceComponent',
props: {
__raw__price: '$2.99'
}
}
In the above example, the __raw__
prefix will be removed, and the unparsed value of $2.99
will be passed as the price
prop to the PriceComponent
.
Another scenario where this comes into play is rendering FormKit components. The <FormKit>
component has a sections-schema
prop that allows users to pass down schema partials to merge with various sections of their input. In this edge case, we want to pass the schema chunks to the <FormKit>
component as a raw JavaScript object. To do so, we once again prefix the sectionsSchema
prop with __raw__
:
Notice if you remove the __raw__
prefix from the above example, the prefix no longer has effect — this is because the sectionsSchema
prop’s value was parsed when creating the component instead of passed as a JavaScript object.
Although schemas can be used for almost any purpose — the primary objective is to empower developers to build complex and dynamic forms using a serializable data format. Using the schema with FormKit Inputs covers this use case well.
Assuming you globally registered the FormKit
component — you can render your FormKit
inputs from schema by using the $cmp
type schema node:
While the cmp
syntax is generalized and works for any Vue component, it is somewhat verbose for FormKit Inputs. To make this easier, FormKit supports a fourth node type $formkit
, which is syntactic sugar for the full $cmp
format.
When using the $formkit
shorthand, the props
object is flattened with the top-level properties (siblings of $formkit
) For example:
The schema format has one built-in function specific to FormKit Inputs: the $get
function. This builtin allows the schema to access the context object of any other FormKit Input (even outside the immediate form) — provided the input in question has an explicitly declared id
prop. This allows the schema to respond conditionally to the state of your own inputs:
To render a form element, you can either use the $formkit: 'form'
schema node, or wrap your <FormKitSchema>
component in a <FormKit type="form">
component: