In this guide, we’ll walk through the process of creating, registering, and using a custom input. Specifically, we’re going create a "one-time password" input ("OTP" for short). OTPs are commonly used for two-factor authentication when a user is required to type in a code sent via SMS or authenticator app. Let’s get started!
.vue
single file components.
To get started, let's create our input’s component file. We'll call it OneTimePassword.vue
:
FormKit provides a lot of input features out-of-the-box that we're going to want to preserve — like labels, help text, and showing error messages. All we really want to modify is the input section of our input. We can preserve these standard FormKit features by using the createInput
utility function from the @formkit/vue
package.
As we build out our input, we’ll want to visualize its progress, so let’s create a sample form to:
OneTimePassword.vue
createInput()
type
prop of a <FormKit>
component.We’ll call this sample form Register.vue
:
Excellent! Now we can iterate on our OneTimePassword.vue
file and see the results. One of the first things to notice is how our input already supports labels, help text, validation, and other universal FormKit props. Those features come courtesy of createInput()
.
Also, notice that <pre>
tag in the above example? It is outputting the current state of the form’s data. We'll use this visualize the value of our custom input. Since our input currently has no value, it does not appear in the form’s data. Time to change that!
Let’s open up OneTimePassword.vue
again and change our <div>
to an <input>
tag. We’ll start with a single text input, and work our way up from there. But how do we actually set and display the value of our custom input?
All custom inputs are passed the almighty context object as the context
prop. In order for our input to set its value, it needs to call context.node.input(value)
. To properly display the value of our input, we should set the input’s :value
attribute to context._value
.
Our little baby input is all grown up! It might not look pretty, but it now reads and writes values. As proof, try setting the initial value of the form’s values
object to { two_factor_code: '12345' }
and you'll see the input gets auto-populated with the value.
Ok, now that we understand how to create an input, how to use it, and how to read and write values — let’s tackle the actual "business logic" of our one-time password input. Here are our requirements:
<input>
tag.For our first requirement, we need n
<input>
tags. Perhaps it would be best to expose the number of digits as a prop. To do that, we need to inform our createInput
function that we want to accept a new prop:
We now have access to context.digits
. Back in OneTimePassword.vue
, let's use that to output the correct number of <input>
tags.
OK — we have multiple inputs! Our first requirement is complete:
<input>
tag.context.classes.yourKey
as the class name of elements in your input.
Notice in the above example that when you type into one input all the other inputs are synced to the same value? Kinda neat, but not what we want. This is because we are still using the same input handler and :value
. Here's a plan to improve our input:
focus()
on the next input.digits
, we update the value of the input by calling context.node.input()
.Great! This is starting to work like we expect. Let’s check our requirements again:
<input>
tag.Looks like we only have one thing left to do — copy & paste support. Fortunately, browsers have a paste
event. To ensure our user experience is top notch, we’ll make an assumption: if a user is copy/pasting they are trying to copy and paste the entire code. Not a single digit of the code. Seems reasonable.
All we need to do is capture the copy/paste event on any of our input tags, get the text being pasted, and set the tmp
value to that string of digits. Let’s whip up another event handler:
Our requirements are all complete!
Now that we've worked up an excellent input, let’s register it with our application so we can use it anywhere by just using the string otp
. Open up your Vue application’s main file (where app.use(formKit)
is). We’ll just add to it:
Done! Now you can use your input anywhere in your application:
Our one-time password input is working great! Here are some ideas for additional features we could flesh out even further:
Hopefully this guide helped you understand how custom inputs are declared, written, and registered. If you want to dive in deeper, try reading about the core internals of FormKit and creating custom inputs!