Components
- AccordionNew
- Alert
- Alert DialogNew
- AutocompleteNew
- AvatarNew
- Badge
- Breadcrumb
- Button
- CalendarNew
- CardNew
- Checkbox
- Checkbox GroupNew
- CollapsibleNew
- ComboboxNew
- CommandNew
- Context MenuNew
- Date PickerNew
- DialogNew
- DrawerNew
- EmptyNew
- FieldNew
- FieldsetNew
- FormNew
- FrameNew
- GroupNew
- Input
- Input GroupNew
- KbdNew
- Label
- MenuNew
- MeterNew
- Number Field
- OTP FieldNew
- Pagination
- PopoverNew
- Preview CardNew
- Progress
- Radio Group
- Scroll AreaNew
- Select
- Separator
- SheetNew
- SkeletonNew
- Slider
- Spinner
- StepperNew
- Switch
- Table
- TabsNew
- Textarea
- ToastNew
- ToggleNew
- Toggle GroupNew
- ToolbarNew
- Tooltip
Form
A form wrapper component that simplifies validation and submission.
"use client";
import { Button } from "mako-ui/components/button";
import { Field, FieldError, FieldLabel } from "mako-ui/components/field";
import { Form } from "mako-ui/components/form";
import { Input } from "mako-ui/components/input";
import type { FormEvent } from "react";
import { useState } from "react";
function stringValue(formData: FormData, name: string): string {
const value = formData.get(name);
return typeof value === "string" ? value : "";
}
export default function Particle() {
const [loading, setLoading] = useState(false);
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
setLoading(true);
await new Promise((r) => setTimeout(r, 800));
setLoading(false);
alert(`Email: ${stringValue(formData, "email")}`);
};
return (
<Form className="flex w-full max-w-64 flex-col gap-4" onSubmit={onSubmit}>
<Field name="email">
<FieldLabel>Email</FieldLabel>
<Input placeholder="you@example.com" required type="email" />
<FieldError>Please enter a valid email.</FieldError>
</Field>
<Button loading={loading} type="submit">
Submit
</Button>
</Form>
);
}
Usage
import {
Field,
FieldError,
FieldLabel,
} from "mako-ui/components/field"
import { Form } from "mako-ui/components/form"
import { Input } from "mako-ui/components/input"<Form
className="flex w-full flex-col gap-4"
onSubmit={(e) => {
/* handle submit */
}}
>
<Field>
<FieldLabel>Email</FieldLabel>
<Input name="email" type="email" required />
<FieldError>Please enter a valid email.</FieldError>
</Field>
</Form>API Reference
Form
A form with no default layout. Pass className for spacing and structure (for example flex w-full flex-col gap-4 for a vertical field stack).
Dialog, sheet, drawer: Place DialogHeader (or sheet/drawer header) outside the form. Wrap DialogPanel and DialogFooter only in <Form className="contents"> (or <form className="contents">). The contents display value keeps panel and footer participating correctly in the popup flex layout without nesting the header inside the <form>.
Examples
Using with Zod
"use client";
import { Button } from "mako-ui/components/button";
import { Field, FieldError, FieldLabel } from "mako-ui/components/field";
import { Form } from "mako-ui/components/form";
import { Input } from "mako-ui/components/input";
import type { FormEvent } from "react";
import { useState } from "react";
import { z } from "zod";
const schema = z.object({
age: z.coerce
.number({ message: "Please enter a number." })
.positive({ message: "Number must be positive." }),
name: z.string().min(1, { message: "Please enter a name." }),
});
type Errors = Record<string, string | string[]>;
function textValue(formData: FormData, name: string): string {
const value = formData.get(name);
return typeof value === "string" ? value : "";
}
async function submitForm(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const result = schema.safeParse(Object.fromEntries(formData));
if (!result.success) {
const { fieldErrors } = z.flattenError(result.error);
return { errors: fieldErrors as Errors };
}
return {
errors: {} as Errors,
};
}
export default function Particle() {
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState<Errors>({});
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
setLoading(true);
const response = await submitForm(event);
await new Promise((r) => setTimeout(r, 800));
setErrors(response.errors);
setLoading(false);
if (Object.keys(response.errors).length === 0) {
alert(
`Name: ${textValue(formData, "name")}\nAge: ${textValue(formData, "age")}`,
);
}
};
return (
<Form
className="flex w-full max-w-64 flex-col gap-4"
errors={errors}
onSubmit={onSubmit}
>
<Field name="name">
<FieldLabel>Name</FieldLabel>
<Input placeholder="Enter name" />
<FieldError />
</Field>
<Field name="age">
<FieldLabel>Age</FieldLabel>
<Input placeholder="Enter age" />
<FieldError />
</Field>
<Button loading={loading} type="submit">
Submit
</Button>
</Form>
);
}
On This Page