Let's start by creating our first FormKit form! We'll learn some of FormKit's key features and how they benefit you. We'll also pick up some nice tips along the way — like how to manage form state without using v-model
.
This guide assumes you are are familiar with the Vue Composition API.
One of the main features of FormKit is its single component API — the <FormKit />
component. This one component gives you access to all input types. And while some types may extend and add features, they share the same base functionality. You can learn more about inputs here.
Even without any props, the bare <FormKit />
component has already given our input a great starting point, with accessible markup, a base text
input type, and additional features that will be explained in later sections.
By default, the <FormKit />
component will use type="text"
if no type
is specified. The type
is how we specify what input we want. Just like native inputs, we have inputs like text
, select
, checkbox
and so on. However, we are not confined to only "native" inputs, FormKit Pro adds non-native controls like the repeater
, taglist
, and autocomplete
types, which can handle more complex interactions.
If you look at the HTML generated by the previous example, you will see that FormKit already created accessible markup. However, as we did not specify the name
and id
properties, they were auto-generated for us: name: "text_1" id="input_0"
. Even so, as a best practice, we should always at least specify the name
as it makes using inputs inside a form easier. The name
is used by the form
and group
types to collect values from, and pass values down to, their children based on the name
:
Our input is still missing some key accessibility functionality like a label
, help
, and maybe even a placeholder
. FormKit accepts all these as props, and outputs the proper aria
attributes. Screen readers will now announce the input name, the help text, and that the input is ready for editing:
Sometimes you want to add an initial value to an input, such as providing a sensible starting place, or populating pre-saved data from a database. We do this with the value
prop.
Let's start building an example that we can add to for this guide. Imagining we are building a "character creation form" for a game. Let's assign our character a strength rating. We could use the range
input with a predefined value of 5
when the users first opens the form:
Validation is one of the main features of FormKit. It helps the user know if the value they are submitting is correct. Adding validation is a breeze, with many powerful built-in validation rules already implemented for you. We will be using the validation
prop to make sure the character is not too strong or too weak. The validation-visibility
prop allows us to control when to show validation messages to the user — whether immediately, when the user blurs the input, or on form submit. The actual validity state is calculated real-time and always up to date — we simply choose when to expose the messages:
Note that the min
and max
props above are built-in browser props for a range input, and represent the top and bottom of the range slider .
Suppose our "backend" requires that data like strength
be cast to a number. By default, FormKit follows HTML "native" inputs behavior, making all values as "strings". To fix that, we can use one of the coolest features of FormKit — plugins — which can be thought of as middleware for inputs. With a plugin, which are just functions, we can change how the value of our input is returned:
First, let's create a basic form and add more inputs so we have content to work with. We will add more features to it in each section, like validation, grouping, and changing values based on other inputs.
We will use one of the inputs called form
, which will make grouping and validation of fields much easier. You just need to wrap all your inputs inside a <FormKit type="form">
:
The form
type will actively collect all the values from child inputs using the name
of each input as a data object for you (just like group
).
The first feature of a form that we'll explore is that we have a @submit
event ready to make our life easier when the time comes to submit our form. The @submit
event gives us as the first argument all the descendant fields the form gathered from the inputs. There is no need to use numerous v-model
s to collect the form data. Let's add our createCharacter()
submit handler:
As a convenience when using type="form"
, the form
outputs a submit button automatically. For our case, a "Submit" text does not show the intent of the form correctly. To fix that, we can use the submit-label
prop, which is a form
-specific feature, simply by adding submit-label="Create Character"
to show the intent of the form:
<FormKit type="form" @submit="createCharacter" submit-label="Create Character">
<!-- Rest of our creation form -->
</FormKit>
While the form works right now, we can see that some related inputs are separated (i.e., the form data is a flat structure where all form data are siblings). Suppose our backend needs all attributes inside an attributes
property. We can use the group
type to group related inputs together by a common name
.
Just like the form
type, you can wrap all yours fields inside a <FormKit type="group" name: "attributes">
. Don't forget to add the name property:
And that is it! We could stop here for an introduction on how forms and inputs work with FormKit. However, let's add some UX enhancements and use that to expose ourselves to additional concepts and features that you can use to take your forms to the next level.
One thing we can do to improve this form is to change the character's default attributes
based on the selected character class
. For that, we will be using some new features:
getNode
gets an input's core node using their id
as an identifier. Each input has an associated core node.events
listen to changes to a certain input.input
function on a node lets us update the value of it.With those features combined, we can get an input's core node
, listen for and respond to events
, and update a value of another field using the input
function:
The code now got a bit less readable, so let's extract the logic to another file to create a plugin instead. Note that we are placing the new updateAttributesPlugin
only on the class
input, so it will not affect any other input. We will also learn another useful feature called traversal by using the at
function of a node:
The at
function uses the name
attribute instead of the id
that getNode
uses.
Let's assume that while different characters are better at different attributes, none should be too powerful. We can do this by creating a budget of points, and adding group validation to the attributes group
to ensure they do not exceed 20 points in totality. We'll learn a new feature — custom rules — to accomplish this:
By default, the group
type does not output any markup, so to show validation errors we need to manually add it.
Sometimes forms need to show or hide fields depending on the value of another input. We can do this by learning 2 new concepts:
FormKit
components receive their context object in the #default
slot prop.group
- The value of a group (and form
) input is an object with the values of its children, keyed by the children's name
s.When using conditional rendering, note that Vue needs hints to know that a DOM element needs a re-render, instead of trying to reuse it. We can add a unique key
property to the element to help Vue.
So, let's grab the context object of the group
input and extract the value
: #default="{ value }"
. We want to add a small easter egg for our users if they decide to change all attributes to 1:
And that concludes our introduction to FormKit! You are now ready to start using it!
There are some topics we recommend you exploring next, which you can read in any order or even later after trying out FormKit for yourself: