Your first form

Introduction

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.

Composition API

This guide assumes you are are familiar with the Vue Composition API.

Robust Vue.js Forms - Vue School Course

1 hr, 49 mins

Our first input

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.

Basic props

The type

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.

Load live example

The name and id

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:

Load live example

Props for accessibility

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:

Load live example

Setting an initial value

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:

Load live example

Adding validation

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:

Load live example

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 .

Adding a plugin

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:

Load live example

Creating the form

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">:

Form values

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).

Load live example

Adding the submit handler

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:

Load live example

Changing the submit button

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:

Load live example

Going deeper

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.

Updating values based on another input

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: getNode gets an input's core node using their id as an identifier. Each input has an associated core node.
  • events: events listen to changes to a certain input.
  • node.input(): the 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:

Load live example

Make it into a plugin

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:

at() uses name

The at function uses the name attribute instead of the id that getNode uses.

Load live example

Adding group validation

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:

Groups do not display messages by default

By default, the group type does not output any markup, so to show validation errors we need to manually add it.

Load live example

Conditional rendering

Sometimes forms need to show or hide fields depending on the value of another input. We can do this by learning 2 new concepts:

  • Context object — We can access an input's value (along with other data) inside our form because all FormKit components receive their context object in the #default slot prop.
  • The value of a group - The value of a group (and form) input is an object with the values of its children, keyed by the children's names.
Vue's key property

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:

Load live example

Next steps

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:

  • Style your forms: Learn how to add classes to FormKit using sections, or even use Tailwind CSS with the help of our guide.
  • Multi-language forms: Learn how FormKit makes i18n easy with dozens of available languages. Or you can add your own!
  • Explore FormKit's schema: Learn about FormKit's JSON-compatible schema and how you can use it for form generation, saving forms to a database in a JSON format, or building your own form builder.