FormKit includes many inputs out of the box, but you can also define your own inputs that automatically inherit FormKit’s value-added features like validation, error messages, data modeling, grouping, labels, help text and others.
Inputs are comprised of two essential parts:
New inputs require an input definition. Input definitions can be registered with FormKit three ways:
FormKitcomponent with the
Input definitions are objects that contain the necessary information to initialize an input — like which props to accept, what schema or component to render, and if any additional feature functions should be included. The shape of the definition object is:
Let’s make the simplest possible input — one that only outputs "Hello world".
Even though this simplistic example doesn’t contain any input/output mechanism, it still qualifies as a full input. It can have a value, run validation rules (they wont be displayed, but they can block form submissions), and execute plugins. Fundamentally, all inputs are core nodes and the input’s definition provides the mechanisms to interact with that node.
To use your custom input anywhere in your application via a "type" string (ex:
<FormKit type="foobar" />) you can add an
inputs property to the
defaultConfig options. The property names of the
inputs object become the "type" strings available to the
<FormKit> component in your application.
Now that we’ve defined our input we can use it anywhere in the application:
The above example extends the
@formkit/inputs library (via
defaultConfig). However, a powerful feature of FormKit is its ability to load input libraries from multiple plugins. These inputs can then be registered anywhere plugins can be defined:
Let’s refactor our hello world input to use its own plugin:
Your input can be written using FormKit’s schema or a generic Vue component. There are pros and cons to each approach:
createInputto extend the base schema section.
The primary takeaway is if you are planning to use a custom input on multiple projects — then consider using the schema-based approach. If your custom input will only be used on a single project and flexibility is not a concern, use a Vue component.
In the future, FormKit may expand to support additional frameworks (ex: React or Svelte. If this is something you are interested in, let us know!.) Writing your inputs using schema means your inputs will be compatible (perhaps minimal changes) with those frameworks too.
All of FormKit’s core inputs are written using schemas to allow for the greatest flexibility possible. You have two primary options when writing your own schema inputs:
It is important to understand the basic structure of a “standard” FormKit input, which is broken down into sections:
input section in the diagram above is typically what you’ll swap out when creating your own inputs — keeping the wrappers, labels, help text, and messages intact. However, if you want to control these aspects as well, you can also write your own input from scratch.
createInputto extend the base schema
To create inputs using the base schema you can use the
createInput() utility from the
@formkit/vue package. This function accepts 2 arguments:
inputsection (see diagram above).
The function returns a ready-to-use input definition.
When providing a component as the first argument,
createInput will generate a schema object that references your component within the base schema. Your component will be passed a single
When providing a schema object, your schema is directly injected into the base schema object. Notice that our hello world example now supports outputting "standard" FormKit features like labels, help text, and validation:
It might make sense to write your inputs completely from scratch without using any of the base schema features. When doing so, just provide the input definition your full schema object.
In the above example, we were able to re-create the same features as the
createInput example — namely — label, help text, and validation message output. However, we are still missing a number of "standard" FormKit features like slot support. If you are attempting to publish your input or maintain API compatibility with the other FormKit inputs, take a look at the input checklist.
For most users, passing a Vue component to
createInput provides a good balance between customization and value-added features. If you’d like to completely eject from schema-based inputs all together, you can pass a component directly to an input definition.
Component inputs receive a single prop — the
context object. It’s then up to you to write a component to encompasses the desired features of FormKit (labels, help text, message display, etc.). Checkout the input checklist for a list of what you’ll want to output.
Inputs have two critical roles:
You can receive input from any user interaction and the input can set its value to any type of data. Inputs are not limited to strings and numbers — they can happily store Arrays, Objects, or custom data structures.
Fundamentally, all an input needs to do is call
node.input(value) with a value. The
node.input() method is automatically debounced, so feel free to call it frequently — like every keystroke. Typically, this looks like binding to the
context object includes an input handler for basic input types:
context.handlers.DOMInput. This can be used for text-like inputs where the value of the input is available at
event.target.value. If you need a more complex event handler, you can expose it using "features".
Any user interaction can be considered an input event. For many native HTML inputs, that interaction is captured with the input event.
The equivalent in a Vue template:
Inputs are also responsible for displaying the current value. Typically, you’ll want to use the
$_value in schema to display a value. This is the "live" non-debounced value. The currently committed value is
$value). Read more about "value settlement" here.
The equivalent in a Vue template:
_valueshould be used is for displaying the value on the input itself — in all other locations, it is important to use the committed
The standard FormKit props that you can pass to the
<FormKit> component (like
type) are available in the root of the context object and in the core node
props, and you can use these props in your schema by directly referencing them in expressions (ex:
$label). Any props passed to a
<FormKit> component that are not node props end up in the
context.attrs object (just
$attrs in the schema).
If you need additional props, you can declare them in your input definition. Props can also be used for internal input state (much like a
ref in a Vue 3 component). FormKit uses the
props namespace for both purposes (see the autocomplete example below for an example of this). Props should always be defined in camelCase and used in your Vue templates with kebab-case.
When extending the base schema by using the
createInput helper, pass a second argument with input definition values to merge:
Features are the preferred way to add functionality to a custom input type. A "feature" is simply a function that receives the core node as an argument. Effectively, they are plugins without inheritance (so they only apply to the current node). You can use features to add input handlers, manipulate values, interact with props, listen to events, and much more.
Features are defined in an array to encourage code reuse when possible. For example, we use a feature called “options” on
As an example, let's imagine you want to build an input that allows users to enter two numbers, and the value of the input is the sum of those two numbers:
Below are some examples of custom inputs. They are not intended to be comprehensive or production ready, but rather illustrate some custom input features.
This is the simplest possible input and does not leverage any of FormKit’s built in DOM structure and only outputs a text input — however it is a fully functional member of the group it is nested inside of and able to read and write values.
$handlers.DOMInputis a built-in convenience function for
(event) => node.input(event.target.value).
Let’s take a look at a slightly more complex example that utilizes
createInput to provide all the standard FormKit structure while still providing a custom input interface.
FormKit exposes dozens of value-added features to even the most mundane inputs. When writing a custom input for a specific project, you only need to implement the features that will actually be used on that project. However, if you plan to distribute your inputs to others, you will want to ensure these features are available. For example, the standard
<FormKit type="text"> input uses the following schema for its
There are several features in the above schema that may not be immediately obvious like the
onBlur handler. The following checklist is intended to help input authors cover all their bases:
labelprop must be displayed and linked for accessibility with the
helpprop must be displayed.
context.messagesobject must displayed if it is set to
node.input(value)when the user provides input. You can use
context.handlers.DOMInputfor text-like inputs.
bind: '$attrs'in schemas.