Introducing FormKit: A Vue 3 form building framework
Tags: vue, javascript, webdev
Published: 2022-02-23 20:16:24


Web forms are the connective tissue of the internet. Without them people can’t request their health records, apply for university, order pad thai, or book a plane ticket. Yet too often the tools used to build those forms are a grab bag of npm packages and DIY solutions that result in a subpar user-experience, poor accessibility, and low maintainability.

In 2020 [I released](https://twitter.com/jpschroeder/status/1236617614618738688?s=20&t=32xC3oOD1p0q_1bm1WY2FA) a small open source library to help alleviate the constant pain of building forms — it was called [Vue Formulate](https://vueformulate.com/). The library was "late to the game" — released for Vue 2 just as Vue 3 was coming on the scene — and developers were already committed to their tools of choice. I had low expectations for adoption *and yet developers started using Vue Formulate — a lot*. It seemed others were experiencing the same pain points as I was.

Let’s review that pain: building high-quality forms is hard. Sure, it’s easy to slap an `<input>` tag on a page, but taking care to label, group, populate, internationalize and validate every input takes a lot of effort — and all too often we leave critically important features on the alter of time: accessibility, error handling, and an empathetic user experience.
 
![Poor validation results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4wafrwmzt02fc1txny9.png)

As a user — how often have you submitted a form only to realize there's a validation error somewhere on the page but the application doesn't tell you which input. Or how about those forms where the developer plops a list of validation errors on the top of the page and expects you to sort it out yourself? How often have *you* been responsible for doing that 😳? Yeah...me too. Personally, I blame the tooling — enter FormKit.

![The FormKit logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8dw1p84x4kyjzoif228.png)

FormKit isn’t just the Vue 3 compatible version of Vue Formulate. It isn’t just another validation library, or UI library — it aims much higher: _FormKit is a form building **framework**_.

## What can it do?

As of today (early 2022), FormKit has a similar feature set to Vue Formulate but with a new architecture that will allow us to ship exciting features on a regular basis. Let’s hit some highlights:

### Single component

FormKit is easy to learn — there's only 1 input component `<FormKit>`. The convenience of typing `<FormKit type="text">` vs `<FormKit type="textarea">` is more profound than it seems on the surface — it provides a consistent shared API for all team members across all your projects.

### No more boilerplate

Form inputs require a hefty dose of markup to be implemented correctly. Honestly, sometimes it’s just more annoying than difficult.

![Developers voting that they don’t like boilerplate on twitter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbg53y1f1e95gmbhbl85.png)
 

FormKit dramatically simplifies boilerplate by providing structured markup out of the box that supports:

- Labels
- Help text
- Validation messages
- Error messages
- Input element
- Wrapper elements

```html
<FormKit
  type="textarea"
  label="Message"
  help="Enter your message"
  validation="required|length:50,200"
/>
```

_Note: You may have to disable your ad-blocker on this page for the following Stackblitz code examples to run._

{% embed https://stackblitz.com/edit/intro-example-a?embed=1&file=src/App.vue&hideExplorer=1&hideNavigation=1&view=preview&ctl=1 %}

### Validation

Although there are some great validation libraries for Vue ([VeeValidate](https://vee-validate.logaretm.com/v4/) and [Vuelidate](https://vuelidate-next.netlify.app/) to name two), FormKit provides pre-written validation rules which are then declared using the `validation` prop. This makes them easier to read and reduces room for developer errors:

```html
<FormKit
  type="password"
  name="password"
  label="New password"
  validation="required|length:6"
  help="Enter a new password, at least 6 characters long"
/>
<FormKit
  type="password"
  name="password_confirm"
  label="Password confirmation"
  validation="required|confirm"
  help="Retype your password"
/>
```

{% embed https://stackblitz.com/edit/intro-example-a-vzy4ja?embed=1&file=src/components/ExampleForm.vue&hideExplorer=1&hideNavigation=1&view=preview&ctl=1 %}

Validation errors are automatically displayed on the inputs that are failing — exactly where your users expect them to be. [There's much more to say about validation](https://formkit.com/essentials/validation), but that’s why FormKit ships with comprehensive docs 😉.

### Form state

Are all the inputs in your form valid? Is your form currently loading? Do you need to disable all the inputs at the same time? No problem. FormKit automatically:

- Adds submit buttons to your forms
- Ensures all inputs pass validation before submitting, and if necessary shows which inputs are still invalid.
- Detects if you’re using an `async` submit handler and sets the form state to loading while awaiting a response.
- Disables all inputs in your form while submission is pending.
- Displays a spinner while submission is pending.

A simple example of this:

```html
<template>
  <FormKit
    type="form"
    @submit="register"
  >
    <h2>Register</h2>
    <FormKit
      name="email"
      label="Email"
      help="What is your email address?"
      validation="required|email"
    />
    <FormKit
      name="password"
      label="Password"
      help="Enter your new account’s password"
      validation="required|length:6"
    />
    <FormKit
      name="password_confirm"
      label="Confirm password"
      help="Re-type that previous password"
      validation="required|confirm"
    />
  </FormKit>
</template>

<script setup>
const register = async (data) => {
  // Let's imagine this is a submit handler
  await new Promise(r => setTimeout(r, 2000))
}
</script>
```

{% embed https://stackblitz.com/edit/intro-example-a-lg4wbr?embed=1&file=src/components/ExampleForm.vue&hideExplorer=1&hideNavigation=1&view=preview&ctl=1 %}

### Model binding

FormKit always knows the precise value of your form and all of its inputs — no matter how large. In fact, you can even `v-model` an entire form with 2-way data binding — this makes repopulating a forms a breeze.

```html
<template>
  <FormKit
    type="form"
    submit-label="Update"
    v-model="values"
  >
    <h2>Edit account</h2>
    <FormKit
      type="text"
      name="first"
      label="First name"
    />
    <FormKit
      type="text"
      name="last"
      label="Last name"
    />
    <FormKit
      name="email"
      label="Email address"
      validation="required|email"
    />
  </FormKit>
  <button @click="randomEmail">Randomize email</button>

  <pre>{{ values }}</pre>
</template>

<script setup>
import { ref } from 'vue'

const values = reactive({
  first: 'Katja',
  last: 'Marabello',
  email: 'katja@gmail.com',
})

const randomEmail = () => {
  const randomString = Math.random().toString(36).substring(2, 10)
  values.email = `${randomString}@example.com`
}
</script>
```

{% embed https://stackblitz.com/edit/intro-example-a-yb1xbj?embed=1&file=src/components/ExampleForm.vue&hideExplorer=1&hideNavigation=1&view=preview&ctl=1 %}

### Plugins

Plugins in FormKit are...powerful. In fact, many of the "advertised" core features, like internationalization (i18n), validation, Vue support, and even the inputs themselves are actually just first-party plugins. That said, the architecture of a FormKit plugin is unique.

Instead of having a centralized instance — like a "FormKit global object" — each input is its own mini-application, complete with its own plugins, store, and children.

Practically speaking, this means that you can expose a completely different set of features to one form on your site vs another — and you can code split and tree-shake those features to give you even better performance for your project.

### Schema

Ahh, finally we come to FormKit’s schema. This is the most requested feature upgrade from Vue Formulate. The FormKit schema is a JSON-serializable data format that can describe virtually any HTML or Vue template structure.

To power this feature, we wrote a small runtime compiler that is able to make quick work of any HTML elements, Vue components, conditional expressions (if/then/else statements), mathematical expressions, boolean logic, loops, slots, and data references. It is so powerful, in fact, that we wrote all of our existing FormKit inputs using it.

Now you can store your forms anywhere you can store JSON — like a database 😉. There is so much more to say about the schema, but again, that’s why [we wrote comprehensive documentation](https://formkit.com/advanced/schema).

# What’s next

FormKit is not feature-complete. Not even close. Although we've achieved feature parity with Vue Formulate (and surpassed it in several important ways), there is a lot more we are working on. Here are some of the highlights:

- Input masking: FormKit was built with input masking in mind and we plan to release a full input masking plugin soon.
- Pro inputs: Our team is hard at work building high quality "synthetic inputs" — inputs that don’t come natively with HTML. Think: autocompletes, tag lists, dropdowns, address fields, repeaters and many more. These will be paid inputs and are how we plan to make the project financially sustainable.
- Theme builder: We support easy-to-use styling with our custom theme (called Genesis), your own CSS, or CSS-utility frameworks like Tailwind. But what if you could use a first-party theme builder to tailor a FormKit theme in-browser to match your project’s design? 🤔
- One more thing...well...many things actually. We’re hard at work on some killer features, but we've gotta keep some tricks up our sleeves 😉.

# Sustainability

You’ve heard me reference "we" and "team" a few times now. So who are these form-building heroes? There is a full team of 6 engineers behind FormKit: [Andrew](https://twitter.com/0xBOYD), [Chris Adams](https://www.instagram.com/scaryonly/), [Chris Ellinger](https://twitter.com/devoidofgenius), [Luan](https://twitter.com/luanguyen), [Sasha](https://www.linkedin.com/in/sasha-milenkovic-b6a4b7168/) and myself [Justin](https://twitter.com/jpschroeder). We’re committed to open source and ensuring that FormKit is forever freely distributed. We have 2 plans to make FormKit a sustainable project:

1. We are accepting sponsors of the FormKit open source project. If you find FormKit to be as useful as we do, [please consider supporting us](https://github.com/sponsors/formkit)!
2. We will soon be releasing a set of "pro" inputs — these will be paid first-party custom inputs. We’re targeting the complex inputs that no one enjoys creating themselves such as autocompletes, tag lists, wysiwygs, and fancy file uploaders. You can [request early access today](https://formkit.com/pro-early-access).

We’re inspired by the great example of folks like [Adam Wathan](https://twitter.com/adamwathan) and [Taylor Otwell](https://twitter.com/taylorotwell) who have built sustainable open source projects that the developer community will enjoy for years to come _because_ they've created adjacent products that support their development efforts. We aspire to do the same with FormKit ❤️.

# So much more...

There’s so much more that isn’t covered in this article so please — checkout the docs at [formkit.com](https://formkit.com/), join us and hundreds other on the FormKit [Discord](https://discord.gg/Vhu97pAC76), and if you’re intrigued and would like to use FormKit in your own projects, throw us a star on [GitHub](https://github.com/formkit/formkit).

🙏 Thank you. We look forward to the road ahead together.