Implementing Image Field Set with Conform and TypeScript
00:00 So, let's go to our edit route and we'll start with our Zod schema. We're gonna make a image field set schema. That's an object that has these fields. So, it's Z shape or object. Thinking of prop types again. And then we've got our ID, which is optional.
00:18 And the file is gonna be just this thing. So, let's grab that. We'll just bring this up here. Copilot could have done that too, but we're just gonna copy and paste. There we go. Okay, great. So, we've got our field set schema here. So, let's add an image property that's assigned to that. Okay, great.
00:36 So, we're gonna immediately start getting some TypeScript errors with this, which is exactly what we are desiring here. So, now we're gonna just grab the image as it is and then pass that image right here. Okay, awesome. So, as far as our backend is concerned,
00:54 we are ready for the new API, I suppose, that you could say that we're gonna be providing to the front end. So, first of all, we actually want to have a default value for this field now because we're gonna pre-fill the field set with this set of fields from our note.
01:13 So, we're gonna say our image is set to data note images at zero. So, again, the data model supports multiple images. We're just doing one for right now. And all of the properties of the image map to our schema for the image.
01:32 So, we have an ID and we have the alt text. So, with that, then we'll be referencing that default down below. Right here, we're gonna pass in fields.image. So, now, instead of passing the image data itself, we're gonna be passing the config and that config will include the default values
01:51 that we just supplied. So, we are, technically, we're still passing down the values for the image, but we're also passing down all the configuration for that image subfield. Again, we've got our note editor schema right here. And so, these fields are going to have those fields
02:10 that we have inside of that image. So, if we take a look at fields.image, then we have all of the different pieces for that to create a field set that is based on that config. Okay, so with that, we can come down here
02:29 and say config is equal to fields.image. And then, in image chooser, we'll swap this with config. So, rename config. And there's that. And then our config type is going to be a little different.
02:50 So, now, this is going to be a field config and that's a type of the image field set schema, but that's not quite right. It needs to be a z-infer of that type. So, that will convert it from a Zod schema type
03:08 into the object that it represents. So, now, our config, we're ready to create a field set based on that config. So, first, to create a field set, we need to associate this field set with the form. And the way that we do that is we create a ref,
03:28 which we're going to pass to the field set. And then that ref will have a form property. So, that field set form object or dom node will have a reference to the form so that we can associate this field set with the form that we're rendering inside of. So, we're going to create a ref.
03:48 Our ref is use ref. And this is a field set element. And then we come down here and we say ref equals ref. There we go. And then we can create a fields object with use field set. So, const fields equals use field set
04:08 from conform to react. And we give it the ref so that we can associate it to the field and the config. Now, the TypeScript's not super jazzed about this because the config is optional, but it's not optional anymore. We have defined the config. It is definitely going to be there.
04:26 The image data may not be there. So, we may not have an existing image anymore or necessarily, but we will definitely have the configuration for these fields. So, we're not going to make that optional. All right, great. So, the existing image should now be based on whether or not we have an ID in our default values.
04:46 So, we have the config for the ID for sure, but whether or not it has a default value will be based on whether or not this image.id exists. And so, if there is no image at index zero, then it won't exist. Okay, great. We'll come right back here.
05:05 So, we are correctly determining whether we have an existing image. And then, for getting that value of that ID, we're going to actually do the exact same thing that we did to determine whether you had an existing image anyway. So, that is all set. And then finally, for our alt text, we're going to also get that
05:24 from the default value as well. Great. So then, we need to update the HTML4 to reference the field's ID, ID. So, that may look funny, but this is the ID of our image. And then, this is the ID of the input element that is going to be accepting the image.
05:45 So, it looks funny, but that's what it is. So, let's add that HTML4 to reference that. There we go. And get rid of this. And then, we'll come down here and we can update this to use the Conform Input Helper, because now this is actually connected to Conform properly.
06:04 So, this will be fields image.id. And yeah, that's actually gonna need to be applied right here. Right there. There we go. And we want to make sure, here, .id. There we go. We want to make sure that our type remains hidden.
06:27 So, we can put it right here as a prop here. Or, you might think we could say type hidden. There as well. And this is not super jazzed about this, because our fields actually just has the ID property.
06:47 So, the image that we had there before was actually the config that we had. So, now we're on fields. And we want to have a field for the ID of the existing image. So, that is what that is. And then, we keep that hidden so that the user doesn't have to look at that ID field. Great.
07:07 Okay, so then, we can remove this ID, because we're going to apply that ID through the use of our Conform helper. So, let's actually, we'll just add the Conform helper here first. Conform.input. And that's gonna be fields.file.
07:30 And yes, we are going to want to specify a type of file. And then, we can get rid of that. Great, so we can save that. And then, we'll update the HTML4 to reference the proper thing. So, here we go with fields.alt.text.id.
07:50 That should be pretty familiar. We've done this already. And then, we can remove the ID, the name, the default value, all of that will be handled for us by our Conform text area utility. And we'll pass our fields.alt.text to that. So, let's just double check that this is working.
08:10 We'll add that. And then, cute, koala, and then submit. And it's all still working. We can still edit as well. So, koala, soccer, and submit. Let's just make sure that the alt text is getting set properly as well. There it is right there.
08:29 So, we're all set. So, let's quickly review what we did, and then we can move on. So, first, we updated our schema. I extracted that so that we could use this to generate the type for our config. So, we have our schema for what an image field set looks like.
08:48 And then, just added that to our schema here. And then, the action updates were simple enough. We just grabbed the image and then passed that as part of our update note call. And then, we updated our note editor use form call so that we added the default value
09:05 so that that's part of conform now. Before, we didn't actually include the image stuff as part of conform at all. So, now, we're including it as a set of complex data structures, so an object. And then, we pass that config down to our image chooser.
09:23 And then, in our image chooser, we accept that config. We created a ref so that we could associate this field set with the form that it's inside. We use the use field set hook from the conform library. We pass our ref to associate it to the form. And we pass our config, which has the type of a field config,
09:44 which comes from conform. And we pass our schema, or the type that our schema represents, so that our fields can have all the fields in the field set that we care about. Then, we updated some of our default values and things here based on the default values
10:04 that we provided to conform in our config. And then, we updated our fields so that they can use the conform helpers instead of just manually associating things. So, what's really cool about this now is it is all type safe as well. So, if I say the file,
10:23 now we're gonna get a bunch of errors and stuff telling us, hey, you need to update this, and that's awesome. And the names will all be properly set and everything. So, we're in a really good place. There's a number of changes that we had to make, but I feel pretty good about them. So, well done, and let's move on.