Fundamental component for inputs.
Pair with Field.* for label/description/error wiring in forms:
Validation States:
1import { Field } from "@ngrok/mantle/field";2import { Input } from "@ngrok/mantle/input";3 4<Field.Item className="max-w-80" name="username">5 <Field.Label>Username</Field.Label>6 <Field.Control>7 <Input placeholder="Enter a username" />8 </Field.Control>9</Field.Item>10<Field.Item className="max-w-80" name="email">11 <Field.Label>Email</Field.Label>12 <Field.Control>13 <Input placeholder="Enter your email" autoComplete="email" />14 </Field.Control>15</Field.Item>16 17<Field.Item name="error">18 <Field.Label>Error</Field.Label>19 <Field.Control>20 <Input validation="error" />21 </Field.Control>22</Field.Item>23<Field.Item name="success">24 <Field.Label>Success</Field.Label>25 <Field.Control>26 <Input validation="success" />27 </Field.Control>28</Field.Item>29<Field.Item name="warning">30 <Field.Label>Warning</Field.Label>31 <Field.Control>32 <Input validation="warning" />33 </Field.Control>34</Field.Item>Or render the control on its own:
1import { Input } from "@ngrok/mantle/input";2 3<Input placeholder="Enter a username" />;Use @tanstack/react-form with zod when the input needs controlled value, blur, submit, and validation state. Field.Item owns the field name and Field.Errors renders normalized validator messages.
1import { Button } from "@ngrok/mantle/button";2import { Field, toErrorMessages } from "@ngrok/mantle/field";3import { Input } from "@ngrok/mantle/input";4import { useForm } from "@tanstack/react-form";5import { z } from "zod";6 7export const formSchema = z.object({8 email: z.string().email("Enter a valid email address."),9});10function Example() {11 const defaultValues = {12 email: "",13 };14 const form = useForm({15 defaultValues,16 validators: {17 onSubmit: formSchema,18 },19 onSubmit: ({ value }) => {20 // Handle form submission here21 },22 });23 24 return (25 <form26 onSubmit={(event) => {27 event.preventDefault();28 event.stopPropagation();29 void form.handleSubmit();30 }}31 >32 <form.Field name="email">33 {(field) => (34 <Field.Item name={field.name}>35 <Field.Label>Email</Field.Label>36 <Field.Control>37 <Input38 type="email"39 value={field.state.value}40 onBlur={field.handleBlur}41 onChange={(event) => field.handleChange(event.target.value)}42 />43 </Field.Control>44 <Field.Errors messages={toErrorMessages(field.state.meta.errors)} />45 </Field.Item>46 )}47 </form.Field>48 <Button type="submit" appearance="filled">49 Submit50 </Button>51 </form>52 );53}You can compose additional visual or functional elements within the Input using children. The examples below show you how to render start and end icons or buttons. The Password Input is built using this API under the hood! Keep in mind that you will need to manually pass the InputCapture component as children too because it is responsible for rendering the actual form input element! We provide an InputCapture component for you when you don't use the children API.
Note: when composing with interactive content (e.g. a button), you will need to consider whether or not that element should be tab-indexable or receive focus!
1import { Field } from "@ngrok/mantle/field";2import { Input, InputCapture } from "@ngrok/mantle/input";3import { InfoIcon } from "@phosphor-icons/react/Info";4import { MagnifyingGlassIcon } from "@phosphor-icons/react/MagnifyingGlass";5 6<Field.Item className="max-w-80" name="search-start">7 <Field.Label>Search with start icon</Field.Label>8 <Field.Control>9 <Input className="max-w-64" placeholder="Search...">10 <MagnifyingGlassIcon />11 <InputCapture />12 </Input>13 </Field.Control>14</Field.Item>15<Field.Item className="max-w-80" name="search-end">16 <Field.Label>Search with end icon</Field.Label>17 <Field.Control>18 <Input className="max-w-64" placeholder="Search...">19 <InputCapture />20 <InfoIcon />21 </Input>22 </Field.Control>23</Field.Item>24<Field.Item className="max-w-80" name="search-both">25 <Field.Label>Search with start and end icons</Field.Label>26 <Field.Control>27 <Input className="max-w-64" placeholder="Search...">28 <MagnifyingGlassIcon />29 <InputCapture />30 <InfoIcon />31 </Input>32 </Field.Control>33</Field.Item>For 90% of cases — a simple input with optional icons or validation — pass children directly to <Input> (or omit children entirely). The Input renders an InputCapture for you internally, so you do not need to import or render it yourself.
InputCapture is the actual <input> element wrapper. Reach for it only when composing custom adornments — for example, a left-side prefix and a right-side suffix where the existing Input slots don't fit. When you supply your own children, you must include <InputCapture /> so the form input element is still rendered. The Password Input is built using this API under the hood.
The Input accepts the following props in addition to the standard HTML input attributes.