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 yours 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-models to collect the form data. Let's add our createCharacter() submit handler:
As 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. We can by simply add 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 and use 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 attributes instead of the id that getNode uses.
Let's assume that while different characters are better at different attributes, that 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 group (and form) input is an object with the values of its children, keyed by the childrens' names.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 user 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: