DEV Community

Cover image for Introducing Vue Formulate — truly delightful form authoring.
Justin Schroeder
Justin Schroeder

Posted on

Introducing Vue Formulate — truly delightful form authoring.

Vue Formulate has been in the wild for 2 months now, and with the latest release (v2.3) the project has enough momentum to warrant a post from its creator (me, Justin Schroeder) on why it exists, what it does, and where it is going.

Quick example of Vue Formulate

The problem with forms

When you’re learning to program, one of the most exciting early progressions is when you make your "Hello World" app interactive by prompting a user for their name. Take those mad I.O. skills to the web and it gets even easier! Just plop an <input> tag into your markup and you’re off the races right? Well...not so fast.

Over the past two months, I've gotten a lot of questions about Vue Formulate. Unsurprisingly one of the most frequent ones is, "What’s wrong with HTML?".

There’s nothing wrong with HTML, of course, just like there was nothing wrong with JavaScript before Vue and React (I know, I know, Vanilla purists’ blood is boiling out there). HTML, React, Vue... it doesn’t matter — the reality is: creating high-quality forms requires a lot of consideration. Labels, help text, validation, inline file uploads, and accessibility are just a few of the items a developer will need to address. This almost inevitably amounts to gobs of copy/paste and boilerplate markup littered throughout your codebase.

There are other issues too. HTML validation, for example, is pretty limited. What if you want to asynchronously check if a username is already taken? What if you want to have well-styled validation errors? What if you want to offer the ability for someone to add more attendees on their ticket purchase? None of these are available to native HTML/React/Vue without considerable effort. Furthermore, maintaining a high level of quality while working on such disparate features becomes secondary to just making the form work. This is fertile ground for a library to help increase developer happiness while pushing quality and accessibility.

Why is Vue Formulate different?

Vue Formulate is far from the first library to address these concerns. Our long-time friends in the community have been fighting these battles for ages: vue-forms, VeeValidate, Vuelidate, and even some UI frameworks like Vuetify aim to help developers author better forms. These are great packages and I wouldn’t discourage you from using them if they’re appropriate for your project. However, Vue Formulate approaches the same problems with two specific objectives:

  1. Improve the developer experience of form authoring.
  2. Increase the quality of forms for end-users.

In order to provide a great developer experience, Vue Formulate needs to focus on being a comprehensive form authoring solution. It cannot just be a validator and doesn’t aspire to become a full UI library. Instead, these guiding principles have resulted in a highly consistent component-first API focused solely on first-class form authoring. To that end, every single input in Vue Formulate is authored with the same component <FormulateInput>, smoothing out the inconsistencies in HTML’s default elements such as <input>, <textarea>, <select> and others. In Vue Formulate you simply tell the <FormulateInput> what type of input it should be — a text input (<FormulateInput type="text">) and a select input (<FormulateInput type="select">) can even be dynamically exchanged by changing the type prop on the fly.

Why is this better you ask? It’s better because it’s easy to remember, fast to compose, and reduces mistakes. We absolutely shouldn’t discount those very real quality of life improvements... but of course that’s not all.

By ensuring all inputs conform to a single component interface we allow for more powerful enhancements like automatic labels, declarative validation, form generation, automatic accessibility attributes, and support for complex custom inputs. This allows a FormulateInput component to maintain an easy-to-use API while being endowed with super powers. Consider how similarly these two inputs are authored using Vue Formulate and yet how different their actual HTML implementation is:

<FormulateInput
  type="email"
  name="email"
  label="Enter your email address"
  help="We’ll send you an email when your ice cream is ready"
  validation="required|email"
/>
<FormulateInput
  type="checkbox"
  name="flavor"
  label="Pick your 2 favorite flavors"
  validation="min:2,length"
  :options="{
   vanilla: 'Vanilla',
   chocolate: 'Chocolate',
   strawberry: ’Strawberry',
   apple: 'Apple'
  }"
/>
Enter fullscreen mode Exit fullscreen mode

Now, notice some of the things we didn’t have to deal with in that example:

  • <label> elements inputs were automatically generated and linked to the <input> element via auto-generated ids (specify if you want).
  • Help text was generated in the proper location and the input was linked to it with aria-describedby.
  • We added real time input validation without having to explicitly output errors.
  • Multiple checkboxes were rendered with their values linked together.
  • The labels for the checkboxes automatically adjusted their position.

By consolidating inputs into a single FormulateInput component, we drastically improve the quality of life for developers, and simultaneously create a powerful hook for adding new features and functionality to those inputs. As a bonus, when it comes time to upgrade to Vue 3’s Composition API, Vue Formulate’s component-first API means developers won’t need to refactor anything in their template code.

Neato, but where’s my form?

I’ve explained Vue Formulate’s purpose and its unique approach to inputs, but how about the form itself? Let’s consider the purpose of the native <form> element: to transmit input from a user to a server by aggregating the values of its input elements. What does that look like in Vue Formulate? Pretty much exactly what you would expect:

<template>
  <FormulateForm
    @submit="login"
  >
    <FormulateInput
      type="email"
      name="email"
      label="Email address"
      validation="required|email"
    />
    <FormulateInput
      type="password"
      name="password"
      label="Password"
      validation="required" 
    />
    <FormulateInput label="Login" type="submit" />
  </FormulateForm>
</template>

<script>
export default {
  methods: {
    login (data) {
      /* do something with data when it passes validation:
       * { email: 'zzz@zzz.com', password: 'xyz' }
       */
      alert('Logged in')
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Great, so data aggregation works just like a normal form, but there’s not anything “reactive” here yet. Ahh, let’s slap a v-model onto that form — and — presto! We have a fully reactive object with all the data in our form.

<template>
  <FormulateForm
    @submit="login"
    v-model="values"
  >
    <FormulateInput
      type="email"
      name="email"
      label="Email address"
      validation="required|email"
    />
    <FormulateInput
      type="password"
      name="password"
      label="Password"
      validation="required" 
    />
    <FormulateInput label="Login" type="submit" />
    <pre>{{ values }}</pre>
  </FormulateForm>
</template>

<script>
export default {
  data () {
   return {
     values: {}
   }
  },
  methods: {
    login (data) {
      /* do something with data:
       * { email: 'zzz@zzz.com', password: 'xyz' }
       */
      alert('Logged in')
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

And yes, v-model means its two-way data binding. You can write values into any input in your form by changing properties on a single object. Aim small, miss small — so let’s shoot for making “it just works” the default developer experience.

Slots, custom inputs, plugins — oh my!

This article is just an introduction — not a substitute for the full documentation — but it wouldn’t be fair to leave out some of my favorite extensibility features. Form authoring tools need to be flexible — there’s an edge case for everything right? Vue Formulate’s highly opinionated component-first API may seem at odds with flexibility, but in reality that consistent API is the core behind a highly flexible architecture.

Slots are a great example of how consistency pays the bills. Central to Vue Formulate’s inputs is a comprehensive context object that dictates virtually everything about an input. The model, validation errors, label value, help text, and lots (lots!) more are members of this object. Because every input has a consistent API, every input has a consistent context object.

While the flexibility to use scoped slots is great — they can hurt the consistency and readability of our form’s code. To address this, Vue Formulate also includes the ability to override the default value of every slot. We call these “Slot Components”, and they’re fundamental to maintaining a clean consistent authoring API. Want to add that example tooltip to every label? No problem. You can replace the default value in the label slot on every input in your project without having to use scoped slots or wrap your components in your templates at all.

If you decide you’re better off creating your own custom input type, you can do that too! Custom inputs keep form authoring buttery-smooth, just pick your own input type and register it with Vue Formulate. Your custom input will get validation, labels, help text, model binding, and more out of the box. Even better, once you’ve created a custom input you can easily turn it into a plugin to share with your team members or the larger community.

Where you go is where I wanna be...

In the excellent Honeypot Vue documentary, Thorsten Lünborg summed up what I consider to be the number one reasons for Vue’s spectacular success:

The focus in Vue.js from the get-go was always that the framework is more than just the code. It’s not like, “this is the library, this is the documentation, of how it works, and now you solve the rest.”

In essence, the Vue core team was willing to go where developers were feeling pain points the most. As a result they have created a framework that isn’t just elegant — it’s delightful for real-world developers to use. Vue Formulate maintains this spirit; to meet developer’s pain points with delightful form authoring solutions. We believe we’ve now paved the road for 90% of users — but if your road is less traveled and you find yourself at an edge case — please shout it out. We're listening.


If you’re intrigued, checkout vueformulate.com. You can follow me, Justin Schroeder, on twitter — as well as my co-maintainer Andrew Boyd.

Top comments (12)

Collapse
 
justinschroeder profile image
Justin Schroeder • Edited

Yeah, this was an intentional decision for the email validator — basically any string can be used by a DNS server, but the vast majority of email address that don’t include a TLD are non-conforming emails. In fact, you'd be hard pressed to find popular sites that allow non-TLD emails (screenshot is facebook).

failing validator on facebook

Importantly though, you can override the default validation rules with your own custom variation — so if that one doesn't work for you no sweat👍

Collapse
 
turbopasi profile image
Pascal Lamers • Edited

Great introduction about what appears to be a great addition to the vue community (although I don't feel the logo, is there a way to contribute on the logo?)

A few questions :

  • what about customization ? Like altering the style or changing the validation messages ? Custom validator ?
  • In all of the form frameworks I end up with putting to much time in changing the style to fit my project. How about adding custom classes ?
Collapse
 
turbopasi profile image
Pascal Lamers

Okey kudos on the documentation , I spent 5 minutes there and got answers to all my questions !

... now about the logo ... 🤔

Collapse
 
justinschroeder profile image
Justin Schroeder

Haha, thanks Pascal! I think we're definitely open to a new logo (I am anyway). The existing one has gotten the project off the ground, but that doesn't mean its set in stone. Shoot me a DM on twitter @jpschroeder and we’ll talk about the logo :)

Collapse
 
ozzythegiant profile image
Oziel Perez

Logo is cool, just needs to be more bubbly in my opinion. Reminds me of soda :D

Collapse
 
ozzythegiant profile image
Oziel Perez

This is excellent! I have been looking for a library with a common sense approach: bundle inputs into a "question" or some logical decision making step. I also love that it makes the components as straight forward as how forms are built in raw HTML.

Collapse
 
justinschroeder profile image
Justin Schroeder

Thanks Oziel! Hopefully Vue Formulate is will do just that for your next project 👍 Happy coding!

Collapse
 
phunky profile image
Mark Harwood

Saw this on Twitter the other day and it looks fantastic, great job on the documentation as well.

I look forward to giving this ago in the future.

Collapse
 
justinschroeder profile image
Justin Schroeder

Thanks Mark! Excited to hear your feedback too.

Collapse
 
chrishappy profile image
Nathan Tsai • Edited

Okay, you convinced me :) Thanks for the informative and engaging article.

Also, is it just me or Firefox, or has the first CodePen stopped working?


'default' is not exported by ../../tmp/codepen/vuejs/src/pen.vue?rollup-plugin-vue=script.js, imported by ../../tmp/codepen/vuejs/src/pen.vue`
Enter fullscreen mode Exit fullscreen mode
Collapse
 
primo19 profile image
Aldrin De Guzman • Edited

This is great! This will save you a lot of libraries to install just to make your forms developer and user friendly. I Will definitely suggest this to other Vue Devs out there! 👍👌

Collapse
 
justinschroeder profile image
Justin Schroeder

Hey I appreciate that Aldrin! I've found It’s actually surprisingly hard to get the word out to developers so any word of mouth is appreciated 👏