Components

Input

A comprehensive collection of input components including text inputs, grouped inputs with addons, shortcode inputs, number inputs, and tag inputs

When to use

✓

Use for free-text single-line entry

Names, emails, URLs, search queries, short answers. Anything that fits on one visible line.

✓

Wrap in a Field component

Field adds the label, helper text, and error display. Always pair Input with Field in forms — never render an Input bare with a separate <label>.

✓

Use the right specialised variant when relevant

InputGroup for addons (currency, units), InputShortcode for templating fields, NumberInput for numeric, TagsInput for multi-value.

✗

Don't use for multi-line content

Anything more than one line → Textarea.

✗

Don't use for predefined option choice

If the user picks from a known list → Select (short) or SelectSearchable (long). Never an Input with a datalist.

✗

Don't roll your own number inputs

Use NumberInput (from @marmoui/ui) — it handles min/max, increment/decrement, and locale-aware formatting.

Choosing the right alternative

You want…Use this
Multi-line free-text (descriptions, notes, body)Textarea
Pick one option from a short known listSelect
Pick one option from a long searchable listSelectSearchable
Pick multiple optionsSelectSearchable with multiple
Multi-value tag/chip entryTagsInput
Numeric input with stepperNumberInput
Input with currency/unit prefixInputGroup
Input with template variable pickerInputShortcode
On/off toggleSwitch
Single yes/no opt-inCheckbox
Search barInput with leftIcon={<MdSearch />}
Date / timeDatePicker

Installation

npm install @marmoui/ui
yarn add @marmoui/ui
pnpm add @marmoui/ui
bun add @marmoui/ui

Usage

Import the input components from the package:

import { 
  Input, 
  InputGroup, 
  InputGroupAddon, 
  InputGroupInput,
  InputGroupButton,
  InputShortcode,
  NumberInput,
  TagsInput
} from '@marmoui/ui';

Basic Input

Default Input

Simple Input

Basic text input

<Input placeholder="Enter text..." />

Input Variants

All Variants

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>

Input Sizes

Size Variants

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>

With Adornments

Start and End Adornments

Icons or text before/after the input

USD
<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

Password Toggle

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

InputGroup allows you to build complex inputs with addons, icons, and buttons.

Basic InputGroup

With Icon Addon

Input with icon prefix

<InputGroup>
<InputGroupAddon>
  <MdSearch size={16} />
</InputGroupAddon>
<InputGroupInput placeholder="Search..." />
</InputGroup>

With Text Addon

URL Input

Input with text prefix

https://
<InputGroup>
<InputGroupAddon>
  <InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="example.com" />
</InputGroup>

With Button

Copy Link Input

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>

InputGroup Sizes

Size Variants

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

InputShortcode provides an input with shortcode picker and optional emoji support, perfect for email subject lines and personalized content.

With Shortcodes

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..."
  />
);
}

With Emoji Support

Shortcode input with emoji picker

<InputShortcode
value={value}
onChange={setValue}
shortcodes={shortcodes}
enableEmoji
placeholder="Type a message with emojis..."
/>

NumberInput

NumberInput provides a numeric input with increment/decrement controls.

Basic Number Input

Quantity selector

function QuantityInput() {
const [value, setValue] = React.useState(50);

return (
  <NumberInput
    value={value}
    onValueChange={setValue}
    min={0}
    max={100}
  />
);
}

With Step and Precision

Temperature input with decimal values

<NumberInput
value={value}
onValueChange={setValue}
min={-50}
max={50}
step={0.5}
precision={1}
/>

TagsInput

TagsInput allows users to enter multiple values as tags or chips.

Basic Tags Input

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>
);
}

Best Practices

✓

Use appropriate variants

Use destructive variant for errors, success for valid states, and warning for cautions.

✓

Provide clear placeholders

Use descriptive placeholder text that indicates the expected input format.

✓

Use icons meaningfully

Icons in adornments should reinforce the input's purpose (search icon for search, lock for password).

✓

Match sizes consistently

Use the same size for related form elements (inputs, buttons, selects).

✗

Don't use too many adornments

Limit adornments to avoid cluttering the input. Usually one start and one end is sufficient.

✗

Don't hide essential information

Don't use placeholder text for important labels or instructions.

Props

Input

PropTypeDefaultDescription
variant'normal' | 'destructive' | 'success' | 'warning''normal'Visual variant
size'sm' | 'md' | 'lg''md'Input size
startAdornmentReactNode-Content before input
endAdornmentReactNode-Content after input
classNamestring-Additional className

InputGroup

PropTypeDefaultDescription
size'sm' | 'md' | 'lg''md'Size of the group
classNamestring-Additional className

InputGroupAddon

PropTypeDefaultDescription
align'inline-start' | 'inline-end' | 'block-start' | 'block-end''inline-start'Position of addon

InputShortcode

PropTypeDefaultDescription
valuestring-Input value
onChange(value: string) => void-Change handler
shortcodesShortcodeGroup[]-Shortcode definitions
enableEmojibooleanfalseEnable emoji picker
onLoadShortcodes() => Promise<ShortcodeGroup[]>-Async shortcode loader

NumberInput

PropTypeDefaultDescription
valuenumber-Current value
onValueChange(value: number) => void-Change handler
minnumber-Minimum value
maxnumber-Maximum value
stepnumber1Increment step
precisionnumber0Decimal precision
variant'normal' | 'destructive' | 'success''normal'Visual variant

Accessibility

Form Labels

Always pair inputs with Label components for accessibility. Use htmlFor to associate labels with inputs.

  • All inputs support standard ARIA attributes
  • InputGroup maintains focus styling for the group
  • TagsInput supports keyboard navigation and deletion
  • NumberInput supports keyboard increment/decrement with arrow keys
  • Adornment buttons should have aria-label for screen readers