Forms Don't Need to Suck

Rate this content

Why form-wide validation schemas are bad for developers and users - and how we can fix this!

20 min
22 Oct, 2021

AI Generated Video Summary

Andy Richardson, a software engineer at GraphCDN, introduces Fielder, a project aimed at simplifying form creation in React. Fielder allows for decentralized form schema and onSubmit logic, making it easier to handle async validation. It eliminates the need for hoisting conditional logic and simplifies rendering and validation. The Fielder library is recommended for exploration but not yet stable for production. The speaker emphasizes the importance of being truthful and addresses the performance and re-rendering capabilities of Fielder.

1. Introduction to Forms and Fielder

Short description:

I'm Andy Richardson, a software engineer at GraphCDN. We specialize in edge caching on GraphQL endpoints. Check out for more information.

♪♪ Damn, what an intro. Thanks, Yanni. Yeah, so like Yanni said, my name's Andy Richardson, I'm going to be talking about some forms today. I am a software engineer currently at GraphCDN. We do some pretty cool stuff with edge caching on GraphQL endpoints, so if you're into that kind of thing, definitely check it out. And yeah, you can find me online, so at I'll probably pop up. Cool. My boss said that I didn't make a big enough statement about GraphCDN, so there you go, max. If you want to find out more as well, he actually did a talk earlier today, this morning, about GraphCDN and what we do, so definitely give that a peek.

2. Rethinking Form Design in React Development

Short description:

Forms should provide a simple, guided, responsive, and fast user experience. React currently handles forms differently, with forms as the primary focus instead of progressive fields. I started a project called Fielder to explore a solution where the form schema and onSubmit logic are not centralized. Let me show you the pieces and examples.

But without further ado, forms. So, first thing I want you to do is just think about a good form experience. So, we've all been on bad form experiences, very confusing all over the place. But when we think about good form experiences, we probably think about a simple journey, a guided experience, so we know where to go throughout the form. And also something that's responsive and fast to use.

I think the worst-case scenario is having to go back in a form because you've made a mistake early on. So, if we put that into an ideal flow, almost like a user journey, we're thinking about populating a field, getting told if you messed up, ideally, as soon as possible, and then some kind of progression. And a progression can be move into the next field, it can be the next page in the form if it's a multiple-page form, or it could be the final progression which is a submission.

And this is what it looks like currently in React when we make forms, which is very, very different. Rather than having these progressive fields which are the primary thing we think about, instead we have forms as the primary thing we think about, and we shove everything in a form early on. And it's kind of strange because that's not what we really do with React. Usually if we have things that happen over a series of pages or so on, we wouldn't put all that logic in one place, but with forms at the moment, we do.

So this got me thinking, is there a way that we can make a form where we don't need to put the schema all in one place? We don't need to put the onSubmit statement all in one place. And what if we do that dynamically as we're moving forward as a unit of users going through fields and so on? And, yeah, so that's exactly what I wanted to try. And a few, about a year and a half ago, I started a project called Fielder with the goal to try something like this. And I want to run you through the pieces and also show you some examples and see what you folks think.

3. Creating Great Forms with Fielder

Short description:

The end goal is to provide a solution for creating great forms without hoisting everything to the top. The key pieces are the form, the field, and progression. Examples will be shown to demonstrate their usage.

The end goal isn't really to get people to use Fielder. It's just so that the next project I go on, I don't have to deal with terrible forms because you all have, I don't know, pushed forward a new library that fixes this problem. So, yeah, this is a proposed solution, but whatever library applies it, doesn't really matter too much.

So, the pieces. So, if we want to create a great form and we don't want to have to hoist everything to the top of the form, first thing you have is the form. But the big difference here is that this is very, very dump. So this is just amalgamation of state and this will be high up in the component tree. And once we declare it, we forget about it.

The next thing is the field and this is our first class citizen. So here we mount the field, we give it some validation, we give it an initial value, and that is, that's basically how we design our forms with fields as opposed to with just one big form. And then finally, we've got progression. And you might think of this as an on submit and it is definitely an on submit, but it can also be an on progress. So if you think about a wizard, if any of you are familiar with that, a form where you've got multiple pages and you're just stepping through, we usually wanna guard that just as much as we wanna guard that final submit statement because there's nothing worse, like we said, than having to go back in a form and fix something somewhere else. So we wanna guard that and prevent progression from happening. So yeah, there's an example, but we can also have some kind of progression which isn't a submit. Cool.

All right, I'm gonna show you some examples. So we'll get hands-on with code and let's see what we can make with those three things. So first off, hopefully you can see this. Yeah, it's big enough. So we've got a username field and we wanna populate it with a value and if the user changes that field or tries to submit and there isn't any value there, we wanna show some kind of error. So, presentational components out the way, that's a completely different talk. The first thing we're gonna do, Declare our field. Pretty straightforward. And then we add our validation. And in this case, because we don't care about the particular event that we're validating on, we're just gonna check the value and if it's not there, we throw an error. And then, as you can see further down, we present that error when it occurs. And then, finally, we just wanna guard that. So, we wanna make sure that that validation is satisfied before we allow any kind of submission. Cool.

4. Adding Async Validation to Forms

Short description:

Let's add async validation to our forms using Fielder. With Fielder, the validation function is called when there's a submit event, making it easier to handle async validation. This eliminates the need to hoist conditional logic into the form declaration and routing, simplifying the code and improving maintainability.

So, now let's mix it up a bit. Let's take what we have already and let's add in some async validation. So, sometimes this can be quite tricky. I think with Formic, you would have to do an onSubmit, which checks, does async validation, and then write your own errors, or something like that. But, in our case, this is the only change. So, because everything is located with a field, the field just gets told, hey, there's a submit event, and the validation function gets called. And then, we just go, okay, well, if it's a submit validation, let's return a promise, which checks if the username is taken. If it is, then we throw an error. If not, we return, and then the handle submit call will continue and submit. And then, yeah, in this case as well, the only difference now is we just wanna show that we're checking that username is taken. So, those are two still fairly basic examples, and they don't really demonstrate why having things coupled to fields as opposed to forms are really valuable. I think branching is when that starts to change. If you've ever tried to do a form, which is technically more than one form, and you need to split depending on user values, you've probably gotten really frustrated with most form libraries. And that's because we're expected to hoist all that conditional logic up into the form when we declare the form. But then we also have that conditional logic in our routing as well, and it becomes a bit of a mess and very hard to maintain.

5. Forms, Validation, and Rendering

Short description:

The user provides a region, and depending on the selection (UK or US), they are asked to enter their favorite tea or coffee, respectively. The forms for UK and US have their own validation logic, eliminating the need for conditional logic. The route component handles the rendering of different steps and ordering them as necessary.

So this first example of a form, the user provides a region. And then if they choose the UK, they get asked to put in their favorite tea. If they hit the US, then they get asked to put in their favorite coffee.

Now, in our UK form, because we only want to validate if we take that branch route, we don't need to worry about hoisting it and then doing lots of conditional logic. Instead, we just mount our fields and add our validation. And similarly, with US form, the exact same thing. And the cool thing about this is that, because we're doing our validation at render time and our submit at render time when we're going down that route as opposed to prematurely, we don't really need to add any conditional logic. Because if we take the UK form route, then we're never going to render that field anyway. So the validation won't apply. So it's a lot more straightforward.

The final step is now just having that route component that just composites this. So handles rendering those different steps and then ordering them in the way that's necessary. So you can see here, we render our region select on the first step. That will then call onComplete. And then we move to the second step. And this is where we start doing our branching. And this is the only branching we do. Because we're handling submission and validation low in the DOM, in the component tree, we don't really need to branch anywhere other than when we're rendering.

6. Rendering and Dynamic Forms

Short description:

The final step is having a route component that composites the different steps and orders them as necessary. We handle submission and validation low in the DOM, eliminating the need for branching logic in multiple places. Treating forms as more dynamic simplifies the process. Check out Fielder if you're interested in exploring this direction.

So it's a lot more straightforward. And the final step is now just having that route component that just composites this. So handles rendering those different steps and then ordering them in the way that's necessary. So you can see here, we render our region select on the first step. That will then call onComplete. And then we move to the second step. And this is where we start doing our branching. And this is the only branching we do.

So if you think about that initial example with Formik, you've got your onSubmit, which will have to branch if you're changing different endpoints to call. And also with your validation, you'll also have to have that conditional logic in your validation. And then when you render, you'll also have to do that branching logic again. Because we're handling submission and validation low in the DOM, in the component tree, we don't really need to branch anywhere other than when we're rendering. And that's kind of the secret, really.

I think that this is one of those things that would have made things a lot easier. And I think it's just, we've gone, as a community, down this route of having forms as this big entity. And I think that maybe it could be a lot simpler if we treat them as more dynamic. So you can basically forget about this. And you can forget about this, all this branching on schemas and on submits. And instead, you can just branch when you're rendering and just declare validation as you would if you weren't branching. So yeah. That's about it, really.

If there was a summarization for this, it's not necessarily check out Fielder, although I'd definitely recommend it if you think this is interesting. But it's more so that let's see if we can get forms going more through this direction. Because I think it has a lot of potential. Thanks.


Passion for UN Forms and Audience Questions

Short description:

Why are you passionate about UN forms? A client's problem with branching forms led me to explore this. Ignoring my partner while working on forms made me commit to finding a solution. Now, let's move on to the audience questions.

Thank you Andy. Now, if you please proceed me to the Q&A lounge. We have some hard-hitting questions for you. Follow me.

Oh yes. Would you like some water? Oh man, I'd love some water. We didn't bring beer. I think that would've been an appropriate time. Was that 6 PM that it starts? Yeah, I think something like that. Yeah. All right.

OK, so first question before we get the questions. What is it with UN forms? What? Why? I mean, as a developer, I understand that there has to be a way to get things done. I mean, as a developer, I understand it's a meat and potatoes thing. You have to do it. But why are you passionate about this? I don't know. I mean, we have to. So Yanni knows this. We have a client who actually was trying to solve this exact problem. And they had forms which were constantly branching. And they had a nightmare trying to hoist all that branching logic. So that's kind of what got me into it. And then after that, I guess it's just, I don't know, when you spend so long ignoring your partner because you're working on forms, it's like you've just got to keep it up now. You've got to commit. Otherwise, she's got questions. So yeah, no. I think just the initial problem that we had with that client really stuck with me. And that's something that I really wanted to explore. Nice. All right, so we have a whole bunch of questions here from the audience.

Favorite Tea and Q&A

Short description:

You went massively under time. What's your favorite tea? I like peppermint tea. React hook form and CreateFielder have different approaches to validation and submit logic. The UK's passenger locator form is frustrating, especially on mobile.

And you also, you've got a new reason for being one of my favorite people. You went massively under time, so we've caught up on being late. Get in. I'll go.

All right. So what's your favorite tea, then? Oh, that's a good question. I like peppermint tea. Oh, nice. My mom actually used to give me tea in a baby bottle, which is probably the most British stereotypical thing ever. That also explains so much. Yeah, yeah. But no, peppermint tea is my current favorite. Nice.

All right, well, let's take this actual technical question. So have you checked out React hook form? And how would you, you know, why go on CreateFielder? What is the deficiency in Nexus instead of the art? No, that's a good question. That came out a bit later than when I started working on this kind of stuff. And it definitely takes some of those approaches to declaring validation and submit logic lower in a form. It still does a lot of hoisting, though. So things like validate on change, validate on blur, you can't do that on a field. You have to do that form wide and declare that. So I think it's definitely a step in the right direction, though, for sure.

Nice. Well, this is the most popular question. I'm sorry, I have to ask this. It's not a technical one. But have you had any experience with the UK's passenger locator form, and could you please fix it? Damn. Is this the one to get back into? Yeah, when you come to the UK, you have to fill this form. And it is a very frustrating experience, especially on mobile. Yeah, I didn't do it on mobile. I have had experience with it.

React Native Support and Origin Story

Short description:

The government form stuff is pretty good because they actually force you to do one field per page. With Fielder, the types that we store are basically whatever gets passed from the input element. React Native support requires pointing the library in the right direction. My problem was more the technical side of things, the implementing, rather than the using forms.

It wasn't too fun. Unusually, the government form stuff is pretty good because they actually force you to do one field per page, which turns out to be quite nice. But yeah, no, I personally haven't had too much experience with that other than just using it once.

Nice. So how does this work with TypeScript? Like, can this work with TypeScript? Can you get type safety with this approach? I think it's been, to be honest, this is something that I haven't worked on for the last couple of months with switching roles. But I'm pretty sure we plugged together some stuff so that when you declare like a field name, then we know what types we're working with. The one thing I would mention was that, at least with Fielder, the types that we store are basically whatever gets passed from the input element. So if you're using a number input element, that still returns a string. So we'll store it as a string. If you want to change that into some kind of integer or whatever, you can do that on SubmitTime.

Nice. Does this work with a React Native? Is there anything DOM-specific here that, you know? Yeah, no, it works with React Native. It wasn't super straightforward, but yeah, no, it does.

Nice. What did you need to do to make it work for React Native? What is the kind of platform-specific thing here? Well, Kadi, if she's in the audience, might know about this because I pestered her a lot. Yeah, it turns out, I mean, you'll notice as well, change handlers in React Native are basically everyone just has a different name for each element. It's onChange here, then it's onSelect somewhere else, and so on. So for React Native support, because there isn't a standard library for form elements, it's mostly about you just need to point the library in the right direction, if that makes sense for your library of choice. But there's pretty detailed docs on that.

Nice. All right, so this is a bit of an open-ended. You alluded to this already with a project in the past, but what is the context of the worst form you have experienced, and what was the most frustrating thing? Basically, I guess the question is, what is your origin story? What made you a joker or a Batman? Oh man, well, I was 10 years old, and I was signing up for Facebook. No, I'm kidding. No. To be honest, I think my problem was more the technical side of things, the implementing, rather than the using forms. I just angled it as a user story, because I thought it would sound better as a story. But in terms of to answer your question, I think it's a technical solution, really, rather than a user solution. But there's no big juicy story where you were tearing a hair out and crying in the middle of the night kind of thing? I'm not weird enough to come up with that on the spot. But if I was, I'd 100% make an origin story. I'll come back to you with a post or something like that.

Code Snippets, Slides, and Fielder Performance

Short description:

There were some questions about code snippets and animations, but the speaker couldn't remember the specific tool. The speaker mentioned finishing their slides yesterday and emphasized the importance of being truthful. Fielder handles performance and re-rendering well, and synchronous validation is synchronous to avoid awkward validation states.

Great. All right. Let's see if there's a couple more questions. There's a lot of questions that are kind of like, I wouldn't say like you know, beside the topic. But what did you use for creating your code snippets and animations. Oh, that's a good question. I found that yesterday. That was code. Can someone shout it out? Someone's going to know it here. What's that? Yeah. Sure. Let's go with that. Something like that. Yeah. Code hike? I'm not sure. I'll tweet it. There's an excuse to follow me.

Andy, are you saying that you made your slides yesterday? I'm saying I finished my slides yesterday. I finished my slides this morning. Most of us don't admit it. But Andy here, you know, he tells the truth and truth only.

How does Fielder handle performance and re-rendering? Is there anything particularly you need to do? Are there any gotchas that you had to solve? No. So one thing I worked to make sure was that synchronous validation is synchronous. And that's the case. That's something that I tried to contribute back to Formic before I started working on this. And I wasn't able to just because there was a lot of stuff going on at the time. But yeah. So synchronous validation is synchronous. You're not going to get those awkward like middle states where valid is true, but then the change event happens. So validation hasn't run yet.

Fielder Library Status and Project Availability

Short description:

That's only for async validation. Anything synchronous is instant. There is a repo with a performance benchmark. The current status of the Fielder library is recommended for checking out, but not stable for production. Adding new fields to forms may cause issues if all fields have the same name. The speaker will be maintaining the project for a few more months. Fielder can be found by searching online, on GitHub, or on NPM.

That's only for async validation. Anything synchronous is instant. And there is a repo with a performance benchmark. So if you want to check it compared to Formic, if you're into performance benchmarks, then there's that. I can't say that I've ever really had a performance problem rendering forms, at least one that required heavy benchmarking. So I'm glad that they exist.

Yeah. Yeah. There's definitely a tick box you can put on your repo readme, the fastest form library. Oh yeah, I like the numbers at least. Exactly. Well, I think that kind of leads us to the next question here, which is what is the current status of the field of the library? Do you recommend us use, you said. You would be OK with another library taking its space? Totally, yeah. Yeah, so what's the status of the project? Would you recommend using it? Would you use it yourself? I'd recommend checking out. I've used it myself for a couple of projects. I think, yeah, I'd definitely say people check it out, but don't feel it's stable for prod. There's one use case which isn't covered, which is when you add new fields to forms as you go, add more, add more, add more. Basically, the problem with that is if you're declaring fields and using the name as the unique identifier, then if all your fields have the same name, you just have them overwriting each other. So if you need it for something like that, maybe think otherwise. But outside of that, it's pretty stable. I'll be maintaining it for a few more months, at least. Nice.

All right, and let's finish here with a softball. Somebody's teed it up you real nice here. Where can we find your project, Andy? My projects? Like Fielder and so on? Fielder, yeah, yeah. If you search Fielder online. Are you telling them to Google it? Google it, basically, yeah. Yeah, that's exactly it. Yeah, there's Docsite and stuff like that. But GitHub or if you search an NPM for Fielder or whatever.

Fielder Search and Conclusion

Short description:

When searching for Fielder, it should come up on any search engine. The speaker doubts that it will rank highly, but they have put in a lot of effort to improve its visibility. Let's move on to the next part.

Basically, any search engine you can find, search Fielder, it'll probably come up. I highly doubt that, actually. If you type Fielder, can somebody please type it really quick and see what happens? Because I'm pretty sure that it's not going to give you a, I think it's going to give you a baseball player or something. I think it would be first page, because I spent ages, ages doing server-side rendering to get it to work so that Google would actually rank me somewhere other than page 50, so, yeah.

All right, we'll settle that score off stage. I think it's time to move on. Thank you so much, Andy. Yeah, thank you very much. Yeah, great talking to you. Appreciate it, man.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2021React Advanced Conference 2021
39 min
Don't Solve Problems, Eliminate Them
Humans are natural problem solvers and we're good enough at it that we've survived over the centuries and become the dominant species of the planet. Because we're so good at it, we sometimes become problem seekers too–looking for problems we can solve. Those who most successfully accomplish their goals are the problem eliminators. Let's talk about the distinction between solving and eliminating problems with examples from inside and outside the coding world.
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Advanced Conference 2021React Advanced Conference 2021
47 min
Design Systems: Walking the Line Between Flexibility and Consistency
Design systems aim to bring consistency to a brand's design and make the UI development productive. Component libraries with well-thought API can make this a breeze. But, sometimes an API choice can accidentally overstep and slow the team down! There's a balance there... somewhere. Let's explore some of the problems and possible creative solutions.
React Summit 2023React Summit 2023
23 min
React Concurrency, Explained
React 18! Concurrent features! You might’ve already tried the new APIs like useTransition, or you might’ve just heard of them. But do you know how React 18 achieves the performance wins it brings with itself? In this talk, let’s peek under the hood of React 18’s performance features: - How React 18 lowers the time your page stays frozen (aka TBT) - What exactly happens in the main thread when you run useTransition() - What’s the catch with the improvements (there’s no free cake!), and why Vue.js and Preact straight refused to ship anything similar
React Summit 2023React Summit 2023
24 min
Debugging JS
As developers, we spend much of our time debugging apps - often code we didn't even write. Sadly, few developers have ever been taught how to approach debugging - it's something most of us learn through painful experience.  The good news is you _can_ learn how to debug effectively, and there's several key techniques and tools you can use for debugging JS and React apps.

Workshops on related topic

React Summit 2023React Summit 2023
170 min
React Performance Debugging Masterclass
Featured WorkshopFree
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents- The different kinds of React application tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting DOM elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RTL tests and how to handle them
Prerequisites- Familiarity with building applications with React- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Testing Library- Machine setup: Node LTS, Yarn
React Day Berlin 2022React Day Berlin 2022
53 min
Next.js 13: Data Fetching Strategies
- Introduction- Prerequisites for the workshop- Fetching strategies: fundamentals- Fetching strategies – hands-on: fetch API, cache (static VS dynamic), revalidate, suspense (parallel data fetching)- Test your build and serve it on Vercel- Future: Server components VS Client components- Workshop easter egg (unrelated to the topic, calling out accessibility)- Wrapping up
React Summit 2022React Summit 2022
160 min
React at Scale with Nx
The larger a codebase grows, the more difficult it becomes to maintain. All the informal processes of a small team need to be systematized and supported with tooling as the team grows. Come learn how Nx allows developers to focus their attention more on application code and less on tooling.
We’ll build up a monorepo from scratch, creating a client app and server app that share an API type library. We’ll learn how Nx uses executors and generators to make the developer experience more consistent across projects. We’ll then make our own executors and generators for processes that are unique to our organization. We’ll also explore the growing ecosystem of plugins that allow for the smooth integration of frameworks and libraries.