Input
The Input component is a single, variant-driven field that switches between different input types based on the variant prop. Instead of importing separate components for each input type, you use one Input and set variant to get the field you need — from a basic text field to a currency input, phone number, date picker, OTP code, or rich text editor.
All variants share common props for labels, helper text, error handling, and disabled state. Each variant also accepts its own specialised props.
Loading Code Block...Full example
Section titled “Full example”A basic OTP input with a label and helper text.
Loading Code Block...Variants
Section titled “Variants”The variant prop determines which input type is rendered. All variants share the same common props (label, helperText, error, disabled, etc.) and each variant adds its own specialised props documented below.
| Variant | Description |
|---|---|
default | Standard text input with optional left/right icons. |
currency | Numeric input with a currency prefix (e.g. GHS). |
mobile-number | Phone number input with country code selector. |
select | Dropdown select with single or multi-select support. |
counter | Numeric stepper with increment/decrement buttons. |
search | Text input styled for search with optional icon placement. |
otp | One-time password input with individual character cells. |
date | Date picker with single date or date range selection. |
rich-text-area | Rich text editor with formatting toolbar. |
Use the variant dropdown below to switch between input types — the preview and the matching code update together:
Disabled
Section titled “Disabled”Set disabled to prevent user interaction. All variants support the disabled state with reduced opacity and cursor-not-allowed.
Loading Code Block...Error state
Section titled “Error state”Set error to true to apply error styling to the field, and pass the message via the errorMessage prop.
Loading Code Block...Helper text with icons
Section titled “Helper text with icons”Use helperText to display guidance below the input. The helperTextIconType prop controls which icon appears alongside the helper text.
| Icon type | Description |
|---|---|
info | Information icon (default) |
warning | Warning triangle icon |
check | Checkmark icon for success/validation |
Use the interactive demo below to switch between helper text icon variants and see the matching code update automatically:
Common Props
Section titled “Common Props”These props are shared across all variants.
| Props | Default | Type | Description |
|---|---|---|---|
| variant | 'default' | 'default' | 'currency' | 'mobile-number' | 'select' | 'counter' | 'search' | 'otp' | 'date' | 'rich-text-area' | Determines which input type is rendered. Each variant activates a different underlying component with its own specialised props. |
| label | - | string | Label text displayed above the input field. |
| placeholder | - | string | Placeholder text shown inside the input when empty. |
| disabled | false | boolean | Disables the input, preventing user interaction. Applies reduced opacity and cursor-not-allowed. |
| required | false | boolean | Marks the field as required. Displays an asterisk next to the label. |
| helperText | - | string | Helper text displayed below the input to provide guidance. Overridden when `error` is provided. |
| helperTextIconType | 'info' | 'info' | 'warning' | 'check' | Icon displayed alongside the helper text. Choose 'info' for guidance, 'warning' for alerts, or 'check' for validation success. |
| error | false | boolean | When true, applies error styling to the input (red border + error helper colour). Use together with `errorMessage` for the message line. |
| errorMessage | - | string | Message displayed below the input when `error` is true. Overrides `helperText` for the message line. Available across Currency, Phone, Select, DatePicker, Counter, Verification (OTP), and the shared `Input`. |
| hint | - | string | Hint text displayed near the label, often used for additional context or character limits. |
| id | - | string | HTML id attribute for the input element. |
| name | - | string | HTML name attribute for form submission. |
Default variant props
Section titled “Default variant props”Additional props available when variant="default".
The default variant also exposes #leftIcon and #rightIcon named slots, which take precedence over the prop-based icon and accept arbitrary slot content.
The leftIcon and rightIcon props accept any ReactNode, so you can pass arbitrary content (icons, custom elements, or composed JSX) instead of just an icon component.
Currency variant props
Section titled “Currency variant props”Additional props available when variant="currency".
| Props | Default | Type | Description |
|---|---|---|---|
| currency | 'GHS' | string | Currency code displayed as a prefix in the input. |
| endsWithZeros | false | boolean | When true, formats the value to always end with two decimal zeros (e.g. 10.00). |
| hint | - | string | Hint text displayed as a tooltip near the currency input. |
Mobile number variant props
Section titled “Mobile number variant props”Additional props available when variant="mobile-number".
| Props | Default | Type | Description |
|---|---|---|---|
| countryCodeDisplayType | 'code' | 'code' | 'flag' | How the country code is displayed — as a dial code string or a flag emoji/icon. |
| intl | false | boolean | When true, enables international phone number format support with country selection. |
Select variant props
Section titled “Select variant props”Additional props available when variant="select".
| Props | Default | Type | Description |
|---|---|---|---|
| options | - | Array<{ label: string; value: string }> | Array of selectable options displayed in the dropdown. |
| multiple | false | boolean | When true, allows selecting multiple options. |
| showSearch | false | boolean | When true, shows a search input within the dropdown to filter options. |
| clearable | false | boolean | When true, allows clearing the currently selected option. |
| wrap | false | boolean | When true, selected values wrap onto multiple lines instead of scrolling horizontally. |
| onSelectionChange | - | (selected: string | string[]) => void | Callback fired when the user selects or deselects an option. |
| isOpen | - | boolean | Controlled open state for the dropdown. When provided, use `onOpenChange` to sync state. |
| onOpenChange | - | (open: boolean) => void | Callback fired when the dropdown opens or closes. |
| capped | - | boolean | When true, limits the height of the multi-select chip container. |
Counter variant props
Section titled “Counter variant props”Additional props available when variant="counter".
| Props | Default | Type | Description |
|---|---|---|---|
| min | 100 | number | Minimum allowed value for the counter. |
| max | 1000 | number | Maximum allowed value for the counter. |
| value | 0 | number | Current value of the counter. |
| step | 1 | number | Increment/decrement step value. |
| controlsPosition | 'center' | 'center' | 'right' | Position of the increment/decrement controls relative to the input. |
| onCounterUp | - | (value: number) => void | Callback fired when the increment (plus) control is activated. Receives the new clamped value. |
| onCounterDown | - | (value: number) => void | Callback fired when the decrement (minus) control is activated. Receives the new clamped value. |
Search variant props
Section titled “Search variant props”Additional props available when variant="search". The variant="search" routes the unified Input to a dedicated SearchInput component under the hood, so you can keep using the unified Input API or import SearchInput directly when you only need the search field.
"@hubtel/react-ui/input"; import {SearchInput} from "@hubtel/react-ui/input";
<Input variant="search" placeholder="Search transactions" showCloseIcon onSearch={(value) => fetchResults(value)} onClear={() => fetchResults("")}/>;
<SearchInput placeholder="Search transactions" showCloseIcon onSearch={(value) => fetchResults(value)} onClear={() => fetchResults("")}/>; ```
<Spacer size="1rem" /><Table client:only="react" data={[ { prop: "leftIcon", type: "ReactNode", default: "-", description: "Icon displayed on the left side of the search input.", reactOnly: true, }, { prop: "leftIcon", type: "Component", default: "-", description: "Icon component displayed on the left side of the search input.", vueOnly: true, }, { prop: "rightIcon", type: "ReactNode", default: "-", description: "Icon displayed on the right side of the search input.", reactOnly: true, }, { prop: "rightIcon", type: "Component", default: "-", description: "Icon component displayed on the right side of the search input.", vueOnly: true, }, { prop: "searchIconPosition", type: "'left' | 'right'", default: "-", description: "Position of the default search icon. Overridden when custom leftIcon or rightIcon is provided.", reactOnly: true, }, { prop: "showCloseIcon", type: "boolean", default: "false", description: "Renders a close (clear) icon when the field has a value. Clicking it triggers `onClear`.", reactOnly: true, }, { prop: "onSearch", type: "(value: string) => void", default: "-", description: "Called with the current value when the user submits a search (Enter key or explicit search affordance).", reactOnly: true, }, { prop: "onClear", type: "() => void", default: "-", description: "Called when the user clears the field via the close icon.", reactOnly: true, }, ]}/>
<Spacer size="2rem" />
### OTP variant props
Additional props available when `variant="otp"`.
<Table data={[ { prop: "inputLength", type: "number", default: "6", description: "Number of individual character cells rendered for the OTP code.", }, { prop: "hint", type: "string", default: "-", description: "Hint text displayed near the OTP input.", }, ]}/>
<Spacer size="2rem" />
### Date variant props
Additional props available when `variant="date"`.
<Table data={[ { prop: "mode", type: "'single' | 'range'", default: "'single'", description: "Selection mode — 'single' for a single date or 'range' for a start/end date range.", }, { prop: "allowPastDates", type: "boolean", default: "false", description: "When true, allows selection of dates in the past.", }, { prop: "minDate", type: "Date | string", default: "-", description: "Minimum selectable date. Dates before this are disabled.", }, { prop: "maxDate", type: "Date | string", default: "-", description: "Maximum selectable date. Dates after this are disabled.", }, { prop: "maxSelectableMonths", type: "number", default: "3", description: "Maximum span in months for date range selection.", }, ]}/>
<Spacer size="2rem" />
### Rich text area variant props
Additional props available when `variant="rich-text-area"`.
<Table data={[ { prop: "editorHeight", type: "string", default: "'100px'", description: "CSS height of the rich text editor area.", }, ]}/>
<Spacer size="2rem" />
## Accessibility
- All input variants render with an associated `<label>` element when the `label` prop is provided, ensuring screen readers can announce the field purpose.- The `required` prop adds visual indication (asterisk) and semantic attributes.- Error messages are displayed inline below the input with appropriate styling for visibility.- Helper text icons (`info`, `warning`, `check`) provide visual cues that complement the text content — the text itself carries the information.- The OTP variant supports keyboard navigation between cells (auto-advance on input, backspace navigation).- The select variant uses keyboard-accessible dropdown controls.
<Spacer size="2rem" />
## Best practices
:::tip[Variant selection]Choose the most specific variant for your use case rather than building custom logic with the `default` variant. For example, use `variant="currency"` for monetary inputs instead of adding a prefix manually.:::
:::tip[Error messages]Provide specific, actionable error messages — "Email must include an @ symbol" is better than "Invalid input". The `error` prop automatically overrides `helperText`, so you don't need to manage both states manually.:::
:::tip[Helper text]Always include `helperText` for complex inputs (currency, date range, OTP) to set expectations about the expected format.:::
:::caution[Variant switching]Do not change the `variant` prop dynamically at runtime based on user input. Each variant initialises its own internal state, and switching variants without a remount can cause unexpected behaviour.:::