- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Forms - Admin Components
The Medusa Admin has two types of forms:
- Create forms, created using the FocusModal UI component.
- Edit or update forms, created using the Drawer UI component.
This guide explains how to create these two form types following the Medusa Admin's conventions.
Form Tooling#
The Medusa Admin uses the following tools to build the forms:
- react-hook-form to easily build forms and manage their states.
- Zod to validate the form's fields.
Both of these libraries are available in your project, so you don't have to install them to use them.
Create Form#
In this section, you'll build a form component to create an item of a resource.
Full Component
Unlike other components in this documentation, this form component isn't reusable. You have to create one for every resource that has a create form in the admin.
Start by creating the file src/admin/components/create-form.tsx
that you'll create the form in.
Create Validation Schema#
In src/admin/components/create-form.tsx
, create a validation schema with Zod for the form's fields:
The form in this guide is simple, it only has a required name
field, which is a string.
Initialize Form#
Next, you'll initialize the form using react-hook-form
.
Add to src/admin/components/create-form.tsx
the following:
1// other imports...2import { useForm } from "react-hook-form"3 4// validation schema...5 6export const CreateForm = () => {7 const form = useForm<zod.infer<typeof schema>>({8 defaultValues: {9 name: "",10 },11 })12 13 const handleSubmit = form.handleSubmit(({ name }) => {14 // TODO submit to backend15 console.log(name)16 })17 18 // TODO render form19}
You create the CreateForm
component. For now, it uses useForm
from react-hook-form
to initialize a form.
You also define a handleSubmit
function to perform an action when the form is submitted.
You can replace the content of the function with sending a request to Medusa's routes. Refer to this guide for more details on how to do that.
Render Components#
You'll now add a return
statement that renders the focus modal where the form is shown.
Replace // TODO render form
with the following:
1// other imports...2import { 3 FocusModal,4 Heading,5 Label,6 Input,7 Button,8} from "@medusajs/ui"9import { 10 FormProvider,11 Controller,12} from "react-hook-form"13 14export const CreateForm = () => {15 // ...16 17 return (18 <FocusModal>19 <FocusModal.Trigger asChild>20 <Button>Create</Button>21 </FocusModal.Trigger>22 <FocusModal.Content>23 <FormProvider {...form}>24 <form25 onSubmit={handleSubmit}26 className="flex h-full flex-col overflow-hidden"27 >28 <FocusModal.Header>29 <div className="flex items-center justify-end gap-x-2">30 <FocusModal.Close asChild>31 <Button size="small" variant="secondary">32 Cancel33 </Button>34 </FocusModal.Close>35 <Button type="submit" size="small">36 Save37 </Button>38 </div>39 </FocusModal.Header>40 <FocusModal.Body>41 <div className="flex flex-1 flex-col items-center overflow-y-auto">42 <div className="mx-auto flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16">43 <div>44 <Heading className="capitalize">45 Create Item46 </Heading>47 </div>48 <div className="grid grid-cols-2 gap-4">49 <Controller50 control={form.control}51 name="name"52 render={({ field }) => {53 return (54 <div className="flex flex-col space-y-2">55 <div className="flex items-center gap-x-1">56 <Label size="small" weight="plus">57 Name58 </Label>59 </div>60 <Input {...field} />61 </div>62 )63 }}64 />65 </div>66 </div>67 </div>68 </FocusModal.Body>69 </form>70 </FormProvider>71 </FocusModal.Content>72 </FocusModal>73 )74}
You render a focus modal, with a trigger button to open it.
In the FocusModal.Content
component, you wrap the content with the FormProvider
component from react-hook-form
, passing it the details of the form you initialized earlier as props.
In the FormProvider
, you add a form
component passing it the handleSubmit
function you created earlier as the handler of the onSubmit
event.
In the FocusModal.Header
component, you add buttons to save or cancel the form submission.
Finally, you render the form's components inside the FocusModal.Body
. To render inputs, you use the Controller
component imported from react-hook-form
.
Use Create Form Component#
You can use the CreateForm
component in your widget or UI route.
For example, create the widget src/admin/widgets/product-widget.tsx
with the following content:
1import { defineWidgetConfig } from "@medusajs/admin-sdk"2import { CreateForm } from "../components/create-form"3import { Container } from "../components/container"4import { Header } from "../components/header"5 6const ProductWidget = () => {7 return (8 <Container>9 <Header10 title="Items"11 actions={[12 {13 type: "custom",14 children: <CreateForm />,15 },16 ]}17 />18 </Container>19 )20}21 22export const config = defineWidgetConfig({23 zone: "product.details.before",24})25 26export default ProductWidget
This component uses the Container and Header custom components.
It will add at the top of a product's details page a new section, and in its header you'll find a Create button. If you click on it, it will open the focus modal with your form.
Edit Form#
In this section, you'll build a form component to edit an item of a resource.
Full Component
Unlike other components in this documentation, this form component isn't reusable. You have to create one for every resource that has an edit form in the admin.
Start by creating the file src/admin/components/edit-form.tsx
that you'll create the form in.
Create Validation Schema#
In src/admin/components/edit-form.tsx
, create a validation schema with Zod for the form's fields:
The form in this guide is simple, it only has a required name
field, which is a string.
Initialize Form#
Next, you'll initialize the form using react-hook-form
.
Add to src/admin/components/edit-form.tsx
the following:
1// other imports...2import { useForm } from "react-hook-form"3 4// validation schema...5 6export const EditForm = () => {7 const form = useForm<zod.infer<typeof schema>>({8 defaultValues: {9 name: "",10 },11 })12 13 const handleSubmit = form.handleSubmit(({ name }) => {14 // TODO submit to backend15 console.log(name)16 })17 18 // TODO render form19}
You create the EditForm
component. For now, it uses useForm
from react-hook-form
to initialize a form.
You also define a handleSubmit
function to perform an action when the form is submitted.
You can replace the content of the function with sending a request to Medusa's routes. Refer to this guide for more details on how to do that.
Render Components#
You'll now add a return
statement that renders the drawer where the form is shown.
Replace // TODO render form
with the following:
1// other imports...2import { 3 Drawer,4 Heading,5 Label,6 Input,7 Button,8} from "@medusajs/ui"9import { 10 FormProvider,11 Controller,12} from "react-hook-form"13 14export const EditForm = () => {15 // ...16 17 return (18 <Drawer>19 <Drawer.Trigger asChild>20 <Button>Edit Item</Button>21 </Drawer.Trigger>22 <Drawer.Content>23 <FormProvider {...form}>24 <form25 onSubmit={handleSubmit}26 className="flex flex-1 flex-col overflow-hidden"27 >28 <Drawer.Header>29 <Heading className="capitalize">30 Edit Item31 </Heading>32 </Drawer.Header>33 <Drawer.Body className="flex max-w-full flex-1 flex-col gap-y-8 overflow-y-auto">34 <Controller35 control={form.control}36 name="name"37 render={({ field }) => {38 return (39 <div className="flex flex-col space-y-2">40 <div className="flex items-center gap-x-1">41 <Label size="small" weight="plus">42 Name43 </Label>44 </div>45 <Input {...field} />46 </div>47 )48 }}49 />50 </Drawer.Body>51 <Drawer.Footer>52 <div className="flex items-center justify-end gap-x-2">53 <Drawer.Close asChild>54 <Button size="small" variant="secondary">55 Cancel56 </Button>57 </Drawer.Close>58 <Button size="small" type="submit">59 Save60 </Button>61 </div>62 </Drawer.Footer>63 </form>64 </FormProvider>65 </Drawer.Content>66 </Drawer>67 )68}
You render a drawer, with a trigger button to open it.
In the Drawer.Content
component, you wrap the content with the FormProvider
component from react-hook-form
, passing it the details of the form you initialized earlier as props.
In the FormProvider
, you add a form
component passing it the handleSubmit
function you created earlier as the handler of the onSubmit
event.
You render the form's components inside the Drawer.Body
. To render inputs, you use the Controller
component imported from react-hook-form
.
Finally, in the Drawer.Footer
component, you add buttons to save or cancel the form submission.
Use Edit Form Component#
You can use the EditForm
component in your widget or UI route.
For example, create the widget src/admin/widgets/product-widget.tsx
with the following content:
1import { defineWidgetConfig } from "@medusajs/admin-sdk"2import { Container } from "../components/container"3import { Header } from "../components/header"4import { EditForm } from "../components/edit-form"5 6const ProductWidget = () => {7 return (8 <Container>9 <Header10 title="Items"11 actions={[12 {13 type: "custom",14 children: <EditForm />,15 },16 ]}17 />18 </Container>19 )20}21 22export const config = defineWidgetConfig({23 zone: "product.details.before",24})25 26export default ProductWidget
This component uses the Container and Header custom components.
It will add at the top of a product's details page a new section, and in its header you'll find an "Edit Item" button. If you click on it, it will open the drawer with your form.