How to create dynamic forms with custom validation in Storyblok and Nuxt.js
Storyblok is the first headless CMS that works for developers & marketers alike.
Web forms are usually some of the least beloved parts of developing a website. In our experience, it has something to do with the combination of styling the forms and tackling form validation.
In this article we’ll show you how to easily set-up dynamic forms with custom validation using Storyblok, Vuelidate and TailwindCSS.
We’re not going to talk about setting up a new Storyblok/Nuxt.js project. That is excellently described in this article. Instead, we focus on the Storyblok fields setup and Vue component code. This step-by-step guide will help you set-up your own dynamic form.
We’ve recorded a video about how the end product looks once you go through the steps in this article. Feel free to check out the video on the end of this article.
Clone the Vue-nuxt-boilerplate repo.
Let’s start with adding Vuelidate to your Nuxt project. This package is responsible for the editable form validation.
1. Install Vuelidate
As described in the Vuelidate setup guide, install the package using npm or yarn
2. Create the Vuelidate plugin file
Create a plugin file
vuelidate.js in the
plugins directory of your project with this content:
3. Add the plugin to your Nuxt config file
Storyblok Validator components
To achieve custom input field validation, create a counterpart of the Vuelidate validators as Storyblok components. In this example we use a few validators from the builtin Vuelidate validators. The cool thing about Vuelidate is that you can create whole new custom validators in addition to the built-in ones. The ones that we going to use are:
- required - requires the input not be empty
- email - requires the input be a valid email
- numeric - requires the input be all numbers
- minLength - requires the input to have a specified minimum length
- maxLength - requires the input to have a specified maximum length
Each of these is represented by a separate Storyblok component.
The component names should be the exact names of the validators since we will map them dynamically to the validators from the Vuelidate package.
required components have one editable field- the custom error message.
maxLength components have an extra param field, which determines the specified length.
Input Field component
We’re going to create a fully dynamic Form component in Storyblok where we add our input fields. Start by creating the input field component. For this tutorial, we’ve chosen to only work with the
<input /> tag, but the setup can be done with all the other tags (textarea, select, etc.) as well. Let's call this component
Here are the fields we want to edit in Storyblok:
- the unique input identificator in HTML
<input name="" />
- the input type
<input type="" />
- created as a
Single-Optiontype in Storyblok with the options: text, tel, num, email to cover the basics
- the label of our input field
- will be used for
<label />in Vue
- the string that gets shown when the input field is empty
<input placeholder="" />
- list of field validators; only our validator components are allowed
As you can see on the screenshot below, we create additional editable fields for styling under the
Styling tab. These contain TailwindCSS classes mapped to their target HTML tags in Vue.
Storyblok Form component
Now we proceed to the Dynamic form component.
inputs field contains the list of inputs fields in our form. The field
formEndpoint is the URL where the form sends the form data. This component has its own Styling tab with a field for custom TailwindCSS classes.
The Submit button tab contains the text and the styling of the submit button.
Vue Component code
After preparing the components and fields on the Storyblok side, we create the Vue component which makes the fully dynamic form possible. We need only one component, named
DynamicForm.global.vue to reflect its Storyblok counterpart. The
.global suffix makes the component globally registered automatically. If you’re not familiar with this, you can read about setting it up in your Nuxt project here.
Next, we'll go through the separate parts of the component and describe their part in the functionality. Let’s start with the
<script> logic part.
Component <script> logic
We need to import only the validators from the Vuelidate package. This makes dynamic validation possible. Using this approach, you can decide on adding a new validator from the built-in ones without needing to change the code, just add a new Storyblok component with the validator's name.
blok represents the Storyblok content. More on why it is here, and how it works, is found in the article mentioned at the beginning.
Our initial form data is generated from the input components using a
.reduce() function. In short, this function creates an object using the form field's unique name as a key. The values are empty string by default.
methods part, we have a function responsible for generating the validators for a given input-field.
Here, we generate an object of validators for a field using the imported
validators variable containing every built-in Vuelidate validator. We use this function in the object which tells Vuelidate which input field has which validators. The ternary operator makes sure the validators' functions that are using params get called with the param as their argument.
fieldRules variable is used as the collection of the fields with their generated validators.
validations root key is where we initiate the Vuelidate frame. You can check out their documentation here. Once the package is mounted, you can access its data and options through
this.$v.form. This is also how we connected to the form data in our last method.
This function is called when the form gets submitted. The
$invalid property has
truthy value when one of the fields doesn't pass its validation. In that case, we make the warnings visible using the
$touch() method. You can read about the built-in props and methods in Vuelidate API.
Connecting all of the dots, we get the following component logic.
The Vue component template in its full glory looks like:
As a whole, we use the TailwindCSS classes as class names for the different parts of our template. The more content we delegate to Storyblok, the more editable the styling gets.
We want to highlight two parts of the template. The first is the
We use the Storyblok-provided
_uid prop as the ID of the field. For binding the input realtime to our data layer and Vuelidate, we connect them by using Vuelidates
$model prop. This is available in our case through
The dynamic class prop enables the styling from
inputField.fieldErrorClass in the case of invalid field content. We know the field has a validation error thanks to
The second part I want to describe is the error message container:
This code iterates through the field validators and shows a custom error message if the iterable validator has a problem. You can read about this functionality here.
That’s it. With that, we’re wrapping up our dynamic form. Combining the parts we’ve talked about, we’ve created the functionality presented in our showcase video.
Storyblok makes even the least popular parts of web development, forms, easy to create and manage. There is a lot more we could show you about the fusion between Storyblok and Vuelidate, but you get the idea. Make sure to contact us if you have any questions or comments about the article. You can find our contact details below.
|How to connect Storyblok with Nuxt.js||https://www.storyblok.com/tp/headless-cms-nuxtjs|