npm install @thrivecart/uiyarn add @thrivecart/uipnpm add @thrivecart/uibun add @thrivecart/uiImport the input components from the package:
import {
Input,
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupButton,
InputShortcode,
NumberInput,
TagsInput
} from '@thrivecart/ui';Basic text input
<Input placeholder="Enter text..." />Different states for validation feedback
<div className="flex flex-col gap-4 w-72">
<Input variant="normal" placeholder="Normal input" />
<Input variant="destructive" placeholder="Error input" />
<Input variant="success" placeholder="Success input" />
<Input variant="warning" placeholder="Warning input" />
</div>Three size options
<div className="flex flex-col gap-4 w-72">
<Input size="sm" placeholder="Small input" />
<Input size="md" placeholder="Medium input (default)" />
<Input size="lg" placeholder="Large input" />
</div>Icons or text before/after the input
<div className="flex flex-col gap-4 w-72">
<Input startAdornment={<MdSearch size={18} />} placeholder="Search..." />
<Input endAdornment={<MdEmail size={18} />} placeholder="Email address" />
<Input
startAdornment={<MdAttachMoney size={18} />}
endAdornment={<span className="text-xs text-ink-light">USD</span>}
placeholder="0.00"
/>
</div>Password input with visibility toggle
function PasswordInput() {
const [showPassword, setShowPassword] = React.useState(false);
return (
<Input
type={showPassword ? 'text' : 'password'}
startAdornment={<MdLock size={18} />}
endAdornment={
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="hover:text-ink transition-colors"
>
{showPassword ? <MdVisibilityOff size={18} /> : <MdVisibility size={18} />}
</button>
}
placeholder="Enter password"
/>
);
}InputGroup allows you to build complex inputs with addons, icons, and buttons.
Input with icon prefix
<InputGroup>
<InputGroupAddon>
<MdSearch size={16} />
</InputGroupAddon>
<InputGroupInput placeholder="Search..." />
</InputGroup>Input with text prefix
<InputGroup>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="example.com" />
</InputGroup>Input with action button
<InputGroup>
<InputGroupAddon>
<MdLink size={16} />
</InputGroupAddon>
<InputGroupInput value="https://example.com/share/abc123" readOnly />
<InputGroupAddon align="inline-end">
<InputGroupButton variant="ghost" size="xs">
<MdContentCopy size={14} />
Copy
</InputGroupButton>
</InputGroupAddon>
</InputGroup>Match input size with your forms
<div className="flex flex-col gap-4 w-72">
<InputGroup size="sm">
<InputGroupAddon><MdSearch size={14} /></InputGroupAddon>
<InputGroupInput size="sm" placeholder="Small search..." />
</InputGroup>
<InputGroup size="md">
<InputGroupAddon><MdSearch size={16} /></InputGroupAddon>
<InputGroupInput size="md" placeholder="Medium search..." />
</InputGroup>
<InputGroup size="lg">
<InputGroupAddon><MdSearch size={18} /></InputGroupAddon>
<InputGroupInput size="lg" placeholder="Large search..." />
</InputGroup>
</div>InputShortcode provides an input with shortcode picker and optional emoji support, perfect for email subject lines and personalized content.
Input with shortcode picker
function ShortcodeExample() {
const [value, setValue] = React.useState('');
const shortcodes = [
{
id: 'contact',
section: 'Contact',
text: 'Contact Fields',
menu: [
{ text: 'First Name', value: '[first_name]' },
{ text: 'Last Name', value: '[last_name]' },
{ text: 'Email', value: '[email]' },
],
},
{
id: 'company',
section: 'Company',
text: 'Company Fields',
menu: [
{ text: 'Company Name', value: '[company_name]' },
{ text: 'Website', value: '[website]' },
],
},
];
return (
<InputShortcode
value={value}
onChange={setValue}
shortcodes={shortcodes}
placeholder="Enter subject with personalization..."
/>
);
}Shortcode input with emoji picker
<InputShortcode
value={value}
onChange={setValue}
shortcodes={shortcodes}
enableEmoji
placeholder="Type a message with emojis..."
/>NumberInput provides a numeric input with increment/decrement controls.
Quantity selector
function QuantityInput() {
const [value, setValue] = React.useState(50);
return (
<NumberInput
value={value}
onValueChange={setValue}
min={0}
max={100}
/>
);
}Temperature input with decimal values
<NumberInput
value={value}
onValueChange={setValue}
min={-50}
max={50}
step={0.5}
precision={1}
/>TagsInput allows users to enter multiple values as tags or chips.
Multi-value input for technologies
function TagsExample() {
const [values, setValues] = React.useState(['React', 'TypeScript']);
return (
<TagsInput value={values} onValueChange={setValues}>
<TagsInputLabel>Technologies</TagsInputLabel>
<TagsInputList>
{values.map(value => (
<TagsInputItem key={value} value={value}>
{value}
</TagsInputItem>
))}
<TagsInputInput placeholder="Add technology..." />
</TagsInputList>
</TagsInput>
);
}Use destructive variant for errors, success for valid states, and warning for cautions.
Use descriptive placeholder text that indicates the expected input format.
Icons in adornments should reinforce the input's purpose (search icon for search, lock for password).
Use the same size for related form elements (inputs, buttons, selects).
Limit adornments to avoid cluttering the input. Usually one start and one end is sufficient.
Don't use placeholder text for important labels or instructions.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'normal' | 'destructive' | 'success' | 'warning' | 'normal' | Visual variant |
size | 'sm' | 'md' | 'lg' | 'md' | Input size |
startAdornment | ReactNode | - | Content before input |
endAdornment | ReactNode | - | Content after input |
className | string | - | Additional className |
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | Size of the group |
className | string | - | Additional className |
| Prop | Type | Default | Description |
|---|---|---|---|
align | 'inline-start' | 'inline-end' | 'block-start' | 'block-end' | 'inline-start' | Position of addon |
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Input value |
onChange | (value: string) => void | - | Change handler |
shortcodes | ShortcodeGroup[] | - | Shortcode definitions |
enableEmoji | boolean | false | Enable emoji picker |
onLoadShortcodes | () => Promise<ShortcodeGroup[]> | - | Async shortcode loader |
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | - | Current value |
onValueChange | (value: number) => void | - | Change handler |
min | number | - | Minimum value |
max | number | - | Maximum value |
step | number | 1 | Increment step |
precision | number | 0 | Decimal precision |
variant | 'normal' | 'destructive' | 'success' | 'normal' | Visual variant |
Form Labels
Always pair inputs with Label components for accessibility. Use htmlFor to associate labels with inputs.
aria-label for screen readers