FormInput
A form input component with built-in label, description, and error handling capabilities.
FormInput
A comprehensive form input component that combines labels, descriptions, and validation states. Perfect for creating accessible, user-friendly forms with proper labeling and helpful context.
Default
The default FormInput includes a label and optional description text, providing clear context for users.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S1:" id=":S1:-label" > Username </label> <div className="relative flex items-center w-full text-gray-11"> <input aria-describedby=":S1:-helper" className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-gray-5 hover:border-gray-8 bg-gray-2 focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0 [&:not(:placeholder-shown)]:focus:ring-0 px-3 py-2" id=":S1:" placeholder="e.g. gandalf_grey" /> </div> <div className="text-[13px] leading-5"> <output className="text-gray-9 flex gap-2 items-center" id=":S1:-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> Choose a unique username for your account </span> </output> </div> </fieldset>
Input States
Required Field
Use the required prop to indicate mandatory fields. This automatically adds an asterisk (*) to the label.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S2:" id=":S2:-label" > Email Address <span aria-label="required field" className="text-error-9 ml-1" > * </span> </label> <div className="relative flex items-center w-full text-gray-11"> <input aria-describedby=":S2:-helper" aria-required className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-gray-5 hover:border-gray-8 bg-gray-2 focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0 [&:not(:placeholder-shown)]:focus:ring-0 px-3 py-2" id=":S2:" placeholder="frodo@shire.me" /> </div> <div className="text-[13px] leading-5"> <output className="text-gray-9 flex gap-2 items-center" id=":S2:-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> We'll send your confirmation email here </span> </output> </div> </fieldset>
Success State
Indicates successful validation or acceptance of input value. The success icon and text provide positive feedback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S3:" id=":S3:-label" > API Key </label> <div className="relative flex items-center w-full text-success-11"> <input aria-describedby=":S3:-helper" className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-success-9 hover:border-success-10 bg-gray-2 focus:border-success-8 focus:ring-2 focus:ring-success-2 focus-visible:outline-none [&:not(:placeholder-shown)]:focus:ring-success-0 px-3 py-2" defaultValue="sk_live_middleearth123" id=":S3:" placeholder="Enter your API key" /> </div> <div className="text-[13px] leading-5"> <output className="flex gap-2 items-center text-success-11" id=":S3:-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> Your API key has been verified </span> </output> </div> </fieldset>
Warning State
Used for potentially problematic inputs that don't prevent form submission. Includes a warning icon and explanatory text.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S4:" id=":S4:-label" > Password </label> <div className="relative flex items-center w-full text-warning-11"> <input aria-describedby=":S4:-helper" className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-warning-9 hover:border-warning-10 bg-gray-2 focus:border-warning-8 focus:ring-2 focus:ring-warning-2 focus-visible:outline-none [&:not(:placeholder-shown)]:focus:ring-warning-0 px-3 py-2" id=":S4:" placeholder="Enter your password" type="password" /> </div> <div className="text-[13px] leading-5"> <output className="flex gap-2 items-center text-warning-11" id=":S4:-helper" > <svg aria-hidden="true" height="12" size="md-regular" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="6" cy="10.125" fill="currentColor" r=".875" strokeWidth="0" /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" x1="6" x2="6" y1="4.75" y2="7.75" /> <path d="m8.625,10.25h1.164c1.123,0,1.826-1.216,1.265-2.189L7.265,1.484c-.562-.975-1.969-.975-2.53,0L.946,8.061c-.561.973.142,2.189,1.265,2.189h1.164" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" /> </g> </svg> <span> Your password is about to expire </span> </output> </div> </fieldset>
Error State
Shows validation errors or other issues that need user attention. Features prominent error styling and message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S5:" id=":S5:-label" > Repository Name </label> <div className="relative flex items-center w-full text-error-11"> <input aria-describedby=":S5:-error" aria-invalid className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-error-9 hover:border-error-10 bg-gray-2 focus:border-error-8 focus:ring-2 focus:ring-error-2 focus-visible:outline-none [&:not(:placeholder-shown)]:focus:ring-error-0 px-3 py-2" id=":S5:" placeholder="my-awesome-project" /> </div> <div className="text-[13px] leading-5"> <div className="text-error-11 flex gap-2 items-center" id=":S5:-error" role="alert" > <svg aria-hidden="true" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="6" cy="10.125" fill="currentColor" r=".875" strokeWidth="0" /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" x1="6" x2="6" y1="4.75" y2="7.75" /> <path d="m8.625,10.25h1.164c1.123,0,1.826-1.216,1.265-2.189L7.265,1.484c-.562-.975-1.969-.975-2.53,0L.946,8.061c-.561.973.142,2.189,1.265,2.189h1.164" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" /> </g> </svg> A repository with this name already exists </div> </div> </fieldset>
Disabled State
Apply when the field should be non-interactive, such as during form submission or based on other field values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S6:" id=":S6:-label" > Organization ID </label> <div className="relative flex items-center w-full text-gray-11"> <input aria-describedby=":S6:-helper" className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-gray-5 hover:border-gray-8 bg-gray-2 focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0 [&:not(:placeholder-shown)]:focus:ring-0 px-3 py-2" defaultValue="org_fellowship123" disabled id=":S6:" placeholder="Organization ID" /> </div> <div className="text-[13px] leading-5"> <output className="text-gray-9 flex gap-2 items-center" id=":S6:-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> Contact admin to change organization ID </span> </output> </div> </fieldset>
With Default Value
Pre-populated input with an initial value that users can modify.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S7:" id=":S7:-label" > Project Name </label> <div className="relative flex items-center w-full text-gray-11"> <input aria-describedby=":S7:-helper" className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-gray-5 hover:border-gray-8 bg-gray-2 focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0 [&:not(:placeholder-shown)]:focus:ring-0 px-3 py-2" defaultValue="The Fellowship Project" id=":S7:" placeholder="Enter project name" /> </div> <div className="text-[13px] leading-5"> <output className="text-gray-9 flex gap-2 items-center" id=":S7:-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> Name of your new project </span> </output> </div> </fieldset>
Read-only State
For displaying non-editable information while maintaining form layout consistency.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor=":S8:" id=":S8:-label" > Generated Token </label> <div className="relative flex items-center w-full text-gray-11"> <input aria-describedby=":S8:-helper" className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-gray-5 hover:border-gray-8 bg-gray-2 focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0 [&:not(:placeholder-shown)]:focus:ring-0 px-3 py-2" defaultValue="tkn_1ring2rulethemall" id=":S8:" placeholder="Your token will appear here" readOnly /> </div> <div className="text-[13px] leading-5"> <output className="text-gray-9 flex gap-2 items-center" id=":S8:-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> Copy this token for your records </span> </output> </div> </fieldset>
Complex Usage
Example of a FormInput with multiple props configured for a specific use case.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
<fieldset className="flex flex-col gap-1.5 border-0 m-0 p-0 max-w-lg"> <label className="text-gray-11 text-[13px] flex items-center" htmlFor="webhook-url-input" id="webhook-url-input-label" > Webhook URL <span aria-label="required field" className="text-error-9 ml-1" > * </span> </label> <div className="relative flex items-center w-full text-gray-11"> <input aria-describedby="webhook-url-input-helper" aria-required className="flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12 border border-gray-5 hover:border-gray-8 bg-gray-2 focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0 [&:not(:placeholder-shown)]:focus:ring-0 px-3 py-2" id="webhook-url-input" placeholder="https://api.yourdomain.com/webhooks" /> </div> <div className="text-[13px] leading-5"> <output className="text-gray-9 flex gap-2 items-center" id="webhook-url-input-helper" > <svg aria-hidden="true" height={14} viewBox="0 0 18 18" width={14} xmlns="http://www.w3.org/2000/svg" > <g fill="currentColor"> <circle cx="9" cy="9" fill="none" r="7.25" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} /> <line fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="9" x2="9" y1="12.819" y2="8.25" /> <path d="M9,6.75c-.552,0-1-.449-1-1s.448-1,1-1,1,.449,1,1-.448,1-1,1Z" fill="currentColor" stroke="none" /> </g> </svg> <span> Enter the URL where we'll send event notifications </span> </output> </div> </fieldset>
Props
The FormInput component extends the standard Input component props with additional form-specific properties:
Prop | Type | Default |
---|---|---|
label | string | - |
description | string | - |
required | boolean | - |
error | string | - |
id | string | - |
className | string | - |
variant | "default" | "success" | "warning" | "error" | - |
Accessibility
FormInput is built with accessibility in mind:
- Labels are properly associated with inputs using htmlFor/id
- Error messages are announced to screen readers using role="alert"
- Required fields are marked both visually and via aria-required
- Helper text is linked to inputs using aria-describedby
- Error states are indicated using aria-invalid
Best Practices
When using the FormInput component:
- Always provide clear, concise labels
- Use description text to provide additional context when needed
- Keep error messages specific and actionable
- Use required fields sparingly and logically
- Group related FormInputs using fieldset and legend when appropriate
- Consider the mobile experience when writing labels and descriptions
- Maintain consistent validation patterns across your form
- Use appropriate input types (email, tel, etc.) for better mobile keyboards
- Consider character/word limits in descriptions and error messages
- Test with screen readers to ensure accessibility
Layout Guidelines
- Labels should be clear and concise
- Error messages should appear immediately below the input
- Description text should be helpful but not too lengthy