#Input
Enter a single line of text.
#Examples
Use controlled value / onChange or uncontrolled defaultValue. Prefix and suffix are decorative content. Standard name, required, type, form and aria-* properties are forwarded to the input; className and style apply to the container.
import { Form, FormField, Input } from '@ifhover/leaf-ui';
import { Mail, Search } from 'lucide-react';
export function InputBasic() {
return (
<Form layout="vertical">
<FormField label="Project name">
<Input aria-label="Project name" placeholder="Name your project" />
</FormField>
<FormField label="Search projects">
<Input aria-label="Search projects" prefix={<Search />} placeholder="Search projects" />
</FormField>
<FormField label="Email">
<Input aria-label="Email" type="email" prefix={<Mail />} placeholder="hello@leaf.design" />
</FormField>
<FormField label="Read-only project name">
<Input aria-label="Read-only project name" defaultValue="Leaf Garden" readOnly />
</FormField>
</Form>
);
}
import { Form, FormField, Input } from '@ifhover/leaf-ui';
export function InputStates() {
return (
<Form layout="vertical" onSubmit={(event) => event.preventDefault()}>
<FormField label="Disabled input">
<Input disabled placeholder="Currently unavailable" />
</FormField>
<FormField label="Project name error" error="Project name is required.">
<Input placeholder="Enter a project name" />
</FormField>
<FormField label="Name warning">
<Input status="warning" defaultValue="A similar name already exists" />
</FormField>
</Form>
);
}
import { Form, FormField, Input, Text } from '@ifhover/leaf-ui';
import { useState } from 'react';
export function InputControlled() {
const [value, setValue] = useState('');
const [submitted, setSubmitted] = useState('');
return (
<Form layout="vertical">
<FormField label="Enter and submit">
<Input
aria-label="Enter and submit"
value={value}
placeholder="Type and press Enter"
onChange={(event) => setValue(event.target.value)}
onPressEnter={() => setSubmitted(value)}
/>
</FormField>
<Text tone="muted" aria-live="polite">
Submitted:{submitted || 'Waiting for input'}
</Text>
</Form>
);
}
import type { ControlSize } from '@ifhover/leaf-ui';
import { Button, Flex, Input, Select, Text } from '@ifhover/leaf-ui';
export function ControlSizes() {
return (
<Flex direction="column" gap="md">
{(['sm', 'md', 'lg'] as const).map((size: ControlSize) => (
<Flex key={size} align="center" gap="sm" wrap>
<Text tone="muted">{size}</Text>
<Input size={size} aria-label={`${size} search`} placeholder="Search projects" />
<Select
size={size}
aria-label={`${size} status`}
defaultValue="all"
options={[
{ label: 'All statuses', value: 'all' },
{ label: 'Active', value: 'active' },
]}
/>
<Button size={size}>Search</Button>
</Flex>
))}
</Flex>
);
}
#Password input
Use type="password" to mask input and show a visibility toggle. Customize the icon or hide the toggle when needed.
import { Form, FormField, Input } from '@ifhover/leaf-ui';
import { Eye, EyeOff, LockKeyhole } from 'lucide-react';
import { useState } from 'react';
export function InputPassword() {
const [visible, setVisible] = useState(false);
return (
<Form layout="vertical">
<FormField label="Password">
<Input
type="password"
aria-label="Password"
placeholder="Enter password"
autoComplete="new-password"
prefix={<LockKeyhole />}
/>
</FormField>
<FormField label="Controlled password">
<Input
type="password"
aria-label="Controlled password"
defaultValue="leaf-garden"
visible={visible}
onVisibleChange={setVisible}
visibilityIcon={(shown) => (shown ? <EyeOff size={16} /> : <Eye size={16} />)}
/>
</FormField>
<FormField label="Disabled password">
<Input type="password" aria-label="Disabled password" defaultValue="leaf-garden" disabled />
</FormField>
<FormField label="No visibility toggle">
<Input
type="password"
aria-label="No visibility toggle"
placeholder="No visibility toggle"
visibilityToggle={false}
/>
</FormField>
</Form>
);
}
#Clear and character count
import { Form, FormField, Input, Text } from '@ifhover/leaf-ui';
import { useState } from 'react';
export function InputClear() {
const [value, setValue] = useState('Leaf Garden');
return (
<Form layout="vertical">
<FormField label="Project name">
<Input
aria-label="Project name"
value={value}
onChange={(event) => setValue(event.target.value)}
allowClear
showCount
maxLength={30}
/>
</FormField>
<FormField label="Keyword">
<Input
aria-label="Keyword"
defaultValue="Leaf"
allowClear
showCount={(text) => `${text.length} characters`}
/>
</FormField>
<Text tone="muted">Current text: {value || 'Empty'}</Text>
</Form>
);
}
#Extended features
Search: No search yet
import { Flex, Input, InputGroup, InputSearch, Select, Space, Text } from '@ifhover/leaf-ui';
import { useState } from 'react';
export function ExtensionDemo() {
const [query, setQuery] = useState('');
return (
<Space direction="vertical" align="stretch">
<label htmlFor="input-extension-1">
Search
<InputSearch
id="input-extension-1"
placeholder="Search projects"
enterButton="Search"
onSearch={setQuery}
/>
</label>
<p role="status">Search: {query || 'No search yet'}</p>
<Flex direction="column" gap="sm" align="center">
<Text>Input group</Text>
<InputGroup aria-label="URL">
<Select
style={{ width: 110 }}
defaultValue="https"
options={[{ value: 'https', label: 'https://' }]}
/>
<Input aria-label="Domain" placeholder="example.com" />
</InputGroup>
</Flex>
</Space>
);
}
#Input API
| Property | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | Control size |
status | 'error' | 'warning' | — | Validation appearance |
prefix / suffix | ReactNode | — | Decorative content before / after the input |
onPressEnter | KeyboardEventHandler<HTMLInputElement> | — | Runs when Enter is pressed |
type | HTMLInputTypeAttribute | 'text' | Input type; use 'password' for passwords |
visibilityToggle | boolean | true | Show the password visibility toggle |
visible / defaultVisible | boolean | false | Controlled / initial password visibility |
onVisibleChange | (visible: boolean) => void | — | Password visibility change |
visibilityIcon | (visible: boolean) => ReactNode | — | Custom visibility toggle icon |
value / defaultValue | string | number | readonly string[] | — | Controlled value / initial uncontrolled value |
onChange | ChangeEventHandler<HTMLInputElement> | — | Read event.target.value |
disabled / readOnly | boolean | false | Disabled / read-only |
ref | Ref<HTMLInputElement> | — | DOM reference |
allowClear | boolean | false | Show clear button |
onClear | () => void | — | After clearing; onChange also fires |
showCount | boolean | ((value: string, maxLength?: number) => ReactNode) | false | Show or customize the character count |
#Types
Import InputProps to describe component props. Option types in the API table link to their detailed fields above.
import type { InputProps } from '@ifhover/leaf-ui';#Forms and localization
Use name to include values in FormData. Uncontrolled fields follow form reset; reset controlled values in application state. Place controls in FormField for labels, help and errors. ConfigProvider supplies regional themes and translated built-in UI. Application labels and options are your responsibility.
#Extension types
import type { InputSearchProps, InputGroupProps } from '@ifhover/leaf-ui';#InputSearchProps API
| Property | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | Size or spacing preset |
status | 'error' | 'warning' | — | Display state |
prefix | ReactNode | — | Content before the input |
suffix | ReactNode | — | Content after the input |
allowClear | boolean | false | Show the clear button |
onClear | () => void | — | Called after clearing the input |
showCount | boolean | ((value: string, maxLength?: number) => ReactNode) | false | Display count or a custom renderer |
visibilityToggle | boolean | true | Show the password visibility toggle |
visible | boolean | — | Controlled password visibility |
defaultVisible | boolean | false | Initial password visibility |
onVisibleChange | (visible: boolean) => void | — | Password visibility change |
visibilityIcon | (visible: boolean) => ReactNode | — | Custom password visibility icon |
onPressEnter | KeyboardEventHandler<HTMLInputElement> | — | Called when Enter is pressed |
onSearch | (value: string) => void | — | Search or Enter callback |
loading | boolean | false | Loading state |
enterButton | ReactNode | — | Search button label; omitted shows an icon |
#InputGroupProps API
| Property | Type | Default | Description |
|---|---|---|---|
compact | boolean | — | Join adjacent controls |