Input
A labelled text input carrying its own label, required marker and error message. type="password" grows a reveal toggle on its own.
'use client'
import * as React from 'react'
import { Input } from '@/components/custom/input'
export default function InputDemo() {
const [email, setEmail] = React.useState('')
return (
<div className="w-full max-w-sm space-y-4">
<Input
label="Work email"
type="email"
required
placeholder="ada@kiotapay.com"
value={email}
onChange={(event) => setEmail(event.target.value)}
error={email.length > 0 && !email.includes('@')}
errorMessage="Enter a valid email address."
/>
<Input label="Password" type="password" required placeholder="••••••••" />
<Input label="Employee number" mode="right" defaultValue="EMP-0142" disabled />
</div>
)
}Installation
pnpm dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/input.jsonnpx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/input.jsonyarn dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/input.jsonbunx --bun shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/input.jsonInstalls components/custom/input.tsx and components/custom/form-field.tsx, the shared label and error chrome.
Usage
'use client'
import { Input } from '@/components/custom/input'
export function EmailField({ form }) {
return (
<Input
label="Work email"
type="email"
required
placeholder="ada@kiotapay.com"
{...form.register('email')}
error={Boolean(form.formState.errors.email)}
errorMessage={form.formState.errors.email?.message}
/>
)
}Props
Extends React.ComponentProps<'input'>.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Required. Rendered above the field and wired to it by id. |
mode | 'left' | 'center' | 'right' | 'left' | Aligns label, control and error together. |
error | boolean | false | Switches the border and ring to the danger ramp. |
errorMessage | ReactNode | — | Shown only while error is true. |
required | boolean | false | Adds the asterisk and the required attribute. |
Behaviour notes
Password fields are not a separate component. type="password" renders the eye toggle, so there is no call site that forgets to add one. The toggle is tabIndex={-1}: it is a convenience, and landing on it between the field and the submit button is a detour for anyone who never needed to see the value.
The asterisk is decorative. It carries aria-hidden, and the control's own required attribute is what assistive technology announces — otherwise the requirement is read out twice.
errorMessage is ignored unless error is set. That keeps a stale message from appearing after the field has been corrected but the copy not yet cleared.