Creating and styling a dynamic form with Storyblok, Next.js, and Tailwind CSS
Storyblok is the first headless CMS that works for developers & marketers alike.
In this article, you will learn how to create and style a dynamic form using Storyblok and Next.js. We will also include Tailwind CSS, a utility first CSS framework. By using these technologies, you can create a dynamic form that is both functional and visually appealing.
The main objectives of this article are to show you how to:
- Integrate Storyblok with Next.js
- Create a dynamic form component using Next.js and Storyblok
- Fetch form fields from Storyblok and render them in the form component
- Style the form using Tailwind CSS
Prerequisites
This article assumes that its readers have the following:
- Experience using Storyblok or any Headless CMS
- Experience with using Next.js. If you are a beginner to this technology, you can read my article about the framework here.
- Basic CSS experience.
- Node.js and Node Package Manager (NPM) installed.
Getting Started
In this section, we will set up Next.js, install all the dependencies needed for the tutorial, and initialize Storyblok into our project.
Setting up Next.js
Create a new Next.js project with:
npx create-next-app@latest
Installing dependencies
First, install Storyblok’s React SDK into your Next.js project:
npm install @storyblok/react
Next, install Tailwind CSS, its form plugin, PostCSS, and Autoprefixer to ensure that your CSS styles are properly processed and optimized for modern browsers:
npm install -D tailwindcss @tailwindcss/forms postcss autoprefixer
Lastly, install react-hook-form, which will help validate all inputs given to the form:
npm install react-hook-form
Creating the form component
In this section, we will create a reusable block component for form inputs which we will use later to create our dynamic form.
The block will contain the following fields: Name, Label, Placeholder, Type, and Validators. All fields will be of type Text except for the “Type” and “Validators” fields of the Type Single-Option and Blocks, respectively.
Go to the Block Library {1} section of your Storyblock space and add a new block. Give the block a technical name of “Form Inputs,” {2} and a type of nestable block {3}, as shown below
Now, define the fields that will be inside the Form Inputs
block. These fields should be the: Name, Label, Placeholder, Type, and Validators, which we mentioned earlier.
Save. Now click on the Type
field to edit. Next, scroll to the bottom and add some options {1}.
Save. Now, we need to add some validators
to our Validators block. We’ll add 5 validators: Email, Minimum Length, Maximum Length, Numeric, and Required. These validators are blocks, so we must create them first. Close the Form Input
window and create these new 5 blocks.
Create the first block, Email
:
Define its fields. Email
should have a single field of type Text
for error messages. Input “errorMessage” and save:
Close the window and create the next block, Minimum Length
. This block should have two fields (“errorMessage” with type Text
and “minLength” with type Number
):
Save and do the same for the Maximum Length block (“errorMessage” with type Text
and “maxLength” with the type Number
), Numeric block (“errorMessage” with type Text
), and Required block (“errorMessage” with type Text
).
Create a new group in the block library called Validators
{1} and move all the validators to that group folder:
Now, go back to the Form Inputs
block and click on Validators
to whitelist the new group we created. Next, scroll to the bottom and click the “Allow only specific components to be inserted“ checkbox. Lastly, click on the “Group(s)” dropdown and add the “Validators” group.
Awesome! Now let’s create our dynamic form. While in the Block Library section of Storyblok, let’s create the form block from here. Create a new block called “Form” with two fields. Name the first “Inputs” of type Blocks
and name the second “Endpoint” of type Text
. Inputs
will contain our custom form fields, and Endpoint
will contain the URL that receives the form data. Whitelist only Form Inputs
in the Inputs
block and save.
Navigate to the Home
story of the ‘Content’ section in Storyblok. First, delete the Teaser
and Grid
boilerplates in the story. Then, add Form
to the Body
block.
Now click on Form
and add some blocks to Inputs
. Remember that we whitelisted Form Inputs
as the only component type to be used inside Inputs
.
Click on Form Inputs
and define the first input for the dynamic form.
In this example, the first name is the input for the first field, and the only validator added is Required
. Click on the Required
validator and input an error message.
Click on the ‘Save’ button at the top and add another validator block for the last name. Add only the Required
validator as well.
We now have 2 form input fields (first and last names). Let’s add two more for email and phone numbers. The content of the email will have a total of 2 validators (Email
and Required
).
The content of the phone number will have a total of 3 validators (Maximum Length
, Minimum Length
, and Numeric
). This is what the error message for the first validator looks like:
Fill in the content with Minimum Length
and Numeric
validators:
Finally, go back to Form
and add a URL to Endpoint
. This endpoint will be used when submitting the form on the front end.
That’s it! Save the changes. In the next section, we will learn how to fetch the form to Next.js.
Fetching the form fields
This section will fetch our dynamic form from Storyblok to our front-end application. Before we start, take a second to look at the Draft JSON {1} of our entire content. You can find this in the dropdown by the side of the Publish {2} button. This will give you an idea of how all Storyblok components are structured.
Return to your Next.js project and create a “components” folder to hold your Storyblok components. Because all the blocks we created are nestable, we can create components for each from the top level (Page) to the last (Validators). The first component we’ll create is for the page. Create a “Page.js” file and paste the following code to map through all the components in its body (Body
block).
Next, we’ll create another file, “Form.js
”, for our Form
component. We’ll use the useForm
hook from “react-hook-form” to validate the form’s inputs and will log all inputted data and the form’s endpoint to our browser console. The next level is the Form Inputs
block we will map through and feed with the register
and error
functions from useForm
.
Create another file, “FormInputs.js
,” for our Form Inputs
component. We will use the register
function, fed to the component to register each input field and feed the name of the input field and any errors to the next level, the Validators
blocks.
We have 5 validators (Email
, Required
, Maximum Length
, Minimum Length
, and Numeric
). So let’s create a new folder inside the components
folder for them. Name the subfolder “validators
.”
Create an Email.js
file in the subfolder for the first validator. Before displaying the error message, we’ll check to see if the error is registered under the email and is of the “pattern
” type:
Next is the Required
validator. Create a Required.js
file with the code below. We’ll check to see if the registered error and the input name are the same before displaying the error:
Next is the Maximum Length
validator:
Then, the Minimum Length validator:
Finally, the Numeric validator:
Now that we have all our components, we have to add them to our Storyblok initialization in the Next.js custom App
component:
What we have done is we have successfully initialized Storyblok with our access token and all of our components using the Storyblok SDK. Now, we can fetch the dynamic form content using the Next.js
’ getStaticProps
method and Storyblok’s getStoryblokApi
function. But first, paste the following code into your index.js
file to display the story containing the dynamic form after it is retrieved from Storyblok:
Underneath this, define the getStaticProps
method and paste the following code to fetch the story.
In your terminal, run this command:
Go to localhost:3000
in your browser. This is what your dynamic form should look like:
This is what it looks like when inputs are invalid:
This is what it looks like when inputs are valid:
The next section will focus on styling the page/component.
Styling the form
In this section, I will teach you how to style your dynamic form with Tailwind CSS. Run the command below to create our CSS configuration files, tailwind.config.js
file and postcss.config.js
:
npx tailwindcss init -p
Configure the template paths in your tailwind.config.js
file and add the forms plugin:
Begin styling by adding the three Tailwind directives and some CSS to globals.css
, located in the styles
folder:
Import globals.css
into your _app.js
file:
Add the following CSS styling to the form tag of Form.js
:
Also, add styling to the submit
input tag:
Open your FormInputs.js
file and add the following styling to the label tag:
Finally, in the same file, wrap the Validators
mapping in a div and add the following style to change the color of all error messages to red:
Here is what the dynamic form looks like now:
Conclusion
By following the steps outlined in this article, you can create and style a dynamic form that is both functional and aesthetically pleasing. The modular architecture and intuitive components provided by Storyblok make content management easy to update and maintain on multiple front-end frameworks, not just Next.js. In addition to creating forms, Storyblok is used for many other use cases, such as building landing pages, product listings, blog posts, and more. So don’t hesitate to experiment and explore all the Storyblok possibilities.
You can check out the complete code for this application on GitHub