In this video, we're going to take a look at SvelteKit Superforms, which is a relatively new library that makes working with forms in SvelteKit so much better. But before we do, let's first take a look at how we might currently be handling forms within our SvelteKit applications. And at surface level, working with forms in SvelteKit is pretty straightforward, right? We define a form with HTML and then we define some type of action on our server side that we're going to submit the form data to, and then everything just works, right? We can then see the form data on our server side and take action based upon that data.
But of course, it's not always that simple. We definitely want to validate user input, so we're going to need to define a schema using some type of schema validation library like Zod. And then inside of our action, we can try to validate the form data against that schema. And then in the event of some type of validation error, we can return fail to pass the error via the action data back to our user on the client side. And then on the client side, we can access that action data using the form prop, which we can then use to render out some error messages or change the color of our borders depending on if there's an error with that specific input field or not. And all of this works as you might expect, but our action data structure is kind of all over the place, right? Nothing beyond my own self-control is forcing me to keep the action data consistent across all the different forms in my app, right? On top of that, adding some progressive enhancement with some client-side validation has the potential to really increase the complexity of our simple form here.
So that's where Superman, I mean Superforms, comes in. So let's reinvent this setup here using SvelteKit Superforms just to see how incredible this library really is. All right, so if you want to check out this repository, it's at ciscoheat/sveltekit-superforms. And thank you to ciscoheat here, as I believe he is the primary main contributor. This package is fantastic. You're doing an amazing job.
I see Joyofcode here is using it already. I should have known. But let's just take a quick, brief look at the documentation. There's a lot here, there's a lot to digest, and I'm not going to be able to cover everything in this video. I'm just going to kind of get you introduced so that you can go off and build great things with it and hopefully contribute to the project as well.
So one of the main things that kind of confused me at first was that it merges page data and action data consistently. So we'll take a look at this in a second. But it also uses Zod for server-side data validation, which is amazing because everybody's using Zod nowadays. It automatically coerces the string data from form data into the correct types. One of the big pains of working with form data is that everything is a string, even if you define it as a number within your input itself. When it gets to the server, of course, it's submitted through the URL so it becomes a string. SvelteKit Superforms takes care of coercing that data back to the type that you intended for it to be. It has support for nested forms as well as being able to send your forms as key-value JSON transparently and so much more. I'm not going to go into all these, it's going to take me forever. But let's just go ahead and get started. So we need to install it with pnpm, and I already have Zod installed and I might already have this installed as well, but let me just go ahead and install this here.
Server-Side Setup with superValidate
Okay, so the first thing that's kind of strange is that inside of our page.server file, we're going to have to do something inside of the load function, right? So we're going to define a form variable and we're going to say await superValidate, which comes from sveltekit-superforms/server. It takes in... this can either be a request event, a request form data, null, or undefined in this first argument here. So we're actually going to pass in the whole request event here, and then it also takes in a schema. So it's a Zod schema. So we'll just add our newContactSchema that we were using earlier like so, and then what we'll do is we're going to return form here.
So then on the client side, we're just going to take in the page data, and then we're going to destructure form from superform, data.form. And this is going to be a store. So we can see here when I hover over this, it already has the right properties that I would expect it to have based on the schema that I defined back here. So what I can do now is I can bind the value of each of these inputs to form.whatever, so $form.firstName, lastName, so on and so forth.
And they also have this really cool component, it's called SuperDebug. So what we can do here, we can just say SuperDebug and it comes from sveltekit-superforms as well, and it wants data, it wants us to pass in the form. So if we look at the form in our web browser here, we can see that we have this new debug component. Of course, it should only be used during development, but it's pretty cool. So we can see in real time as we're typing what is happening to our form, as well as the page status up here in the top right. So a really interesting feature, I thought it was pretty cool that they added this. It makes it super easy just to see what's going on as you're working with your different forms.
So now let's take a look at how we can use it when we actually submit the form, right? So let's clear all of this out here. I don't want any of this, and I don't even want this either. Again, we're going to set form is going to be await superValidate. We're going to pass in the event again, as well as our newContactSchema. And then we can console log this form here. So if I open up my console and I go and submit this form and we check out our console here, we're going to have a nice object that gets console logged here with a few different properties. One of which is valid, which means was the data validation successful. We have errors, which I assume will be populated whenever we have the errors. And we also have the original form data as well as the constraints which it received and inferred from Zod.
So it's now submitted an invalid form. So I'll leave first name empty, I'll type in last name, and I'll leave company empty as well. So now we can see that we get valid as false, we get the errors here in a nice consistent structure, we get the data, and then all the rest of the same stuff that we had mentioned earlier. So all we have to do here, which is insane to think about how much code we just had, is we say, if the form is not valid, then we're going to return fail with the status code of 400 and we're just going to return the form. And then even if it is valid, we're still going to return the form. It's just not going to be a failure, right? And let's just submit an invalid form again. We're going to see that the status code has changed in the top right to 400, and then we have our data that was returned back without having to do anything else.
Displaying Validation Errors on the Client
Now let's say we want to access those errors, right? Well, all we have to do is bring the errors in here. So then if we wanted to show some type of error message for each one of the individual inputs if they have an error, we could simply do, if $errors which is also a store, .firstName, then we want to display the $errors.firstName. And we can do the same thing for each of these inputs here like so. And then if I come back to my browser here, we're going to see that the error messages are now being displayed here. And of course, if I updated my Zod object with prettier error messages, those will be displayed here instead of these default Zod error messages.
And so right now, we're still not using JavaScript to submit this form. So we click submit, we're going to see that the page refreshes. So how do we use the enhance action with this?
Progressive Enhancement with use:enhance
Well, it's simple. All we have to do is bring in enhance here, go to our form, say use:enhance like so. And now when I submit this form, it's 100% done on the client side. You can see no browser refreshes, no navigations, nothing. And what this does, it actually unlocks a ton of additional features. And this is really where the feature list goes on and on and on and on.
So if I scroll down a bit here where they talk about importing the enhanced, we can see that we get additional options. So one of which is a tainted form check. So to demonstrate what this is really quick, we can bring in the object, we say taintedMessage or taintMessage. 'Make sure you want to leave'. And then if we go into our SvelteKit application, I type in some stuff, Hello, whatever, it doesn't really matter, and I try to hit the back button, we're going to see that we get this message here. And I think it really may depend on what browser you're using what message shows up. But for some reason right now, it's not showing my custom message. It's just showing some type of default message here. So that's pretty cool that it does in fact pop this up though. Additionally, there's auto scroll and autofocus on errors and a ton of other stuff as well.
Client-Side Validation with Constraints
So the last thing I'm going to cover in this video is client-side validation. That's been a hot topic in my comment section over the past multiple videos that I've created. And SvelteKit Superforms gives you a few different options here as to how you want to validate your client-side form.
So they mentioned that there's already a browser standard for client-side form validation. You all should be familiar with this. If you're not, it's basically like, hey, when you type in a type of email and you don't actually submit an email address, it doesn't include an '@' something, then it's going to pop up that HTML message. So there's a ton of these different ones already baked in and that's where these constraints come from, right? So let's first take a look at the constraints, which is fantastic out of the box. So we can come into this destructured object here and say constraints. And then we can go into each one of these inputs and just spread the constraints.whateverTheInputNameIs, so ...$constraints.firstName. And then I'll just copy this and do the same thing for each of these other inputs as well. Okay, so I have all the constraints being spread here. So now let's go into our form and let me just try to leave all this blank and click submit. We're going to see that I'm getting the 'Please fill out this field'. Okay, I have to fill out this one as well. And then if I just type in something random, not an actual email address, of course, it's going to tell me to please include an '@' in the email address again. So this is the, you know, bare minimum web standard for input validation, right?
Advanced Client-Side Validation Methods
But they also give us a couple other options here as well, one of which is their validator object, which if we look here, we see that we have this validators, which can either be any Zod object. So we can use Zod, it just adds on to the client size's weight, or we can use their validation syntax here. So we'll start out with their validation syntax. Let me just remove these constraints here. And then within this object here, this options object, we can set up validators. And then it's going to be an object with the different properties of our form. So we can have firstName, which takes in a first name, and what we can do is we can say firstName.length is less than 1, and we want to show some type of error message, 'Your name is too short', otherwise null. And we can do the same thing for the rest of them as well if we wanted to. I'll just demonstrate the first name to keep things short and sweet.
So then if we come into our form and I just leave that blank and try to submit this, we're going to get our custom message here, 'Your name is too short'. Additionally, we could bring in our Zod schema. So you should probably put this in some type of central location if you're going to use it in both places, but we'll just copy it over for now. And I need to import Zod here. And then instead of passing this, I can just pass in the newContactSchema like so. And then now, as we go to submit the form, we can start to see that these different validations are occurring just as they would if we were to submit the form all the way to our server, except it's happening on the client side, right?
Final Thoughts & Conclusion
So I think this library is absolutely fantastic. I am super stoked to use it on my future projects. I've already started to refactor a couple of existing projects to use it, just because it just reduces so much code and just allows me to not to think about how I'm structuring those action data objects. And I haven't really found a downside to it yet. So definitely go check out this library, contribute to the library, point out any bugs that you find, raise any issues for feature requests. It seems like they're really looking for feedback. As always, thank you so much for watching and I will see you in the next one.