InputBlock
Text field with an icon at the end and room for a prefix before it, inside one border. It wraps
TextInput and IconButton in a group
that draws the border for them.
Use this when / not when
- Use for a field that needs an affordance at its end — a clear cross, a reveal eye, a picker — or a fixed prefix in front of it.
- Not for a plain field.
TextInputis that, and this always renders the icon box unless you saynoIcon. - Not for search —
SearchInputis this component with the search and clear behaviour already wired, and itsonChangegives you a string. - Not for a password —
PasswordInputadds the reveal toggle and the strength rules.
Import
import { InputBlock } from "@onlyoffice/apps-ui-kit/components/input-block";
Also exported from the root barrel @onlyoffice/apps-ui-kit.
Needs ThemeProvider from @onlyoffice/apps-ui-kit/providers/theme for the border and
background colours.
Stories
Default
A text field with a search icon at its end, inside one border. Type in it, and change any other prop live in the Controls panel below.
Sizes
Match the field to the controls around it: Base size, Middle size and Large size differ in text size, padding and the height of the icon box (size).

States
How the field reads in each state: Error state and Warning state recolour the border of the whole group (hasError, hasWarning); Disabled greys the field out and drops the icon (isDisabled); Read-only content keeps the look and the icon and only stops typing (isReadOnly).

Password Type
For a secret that must not be read off the screen, the typed characters are masked (type={InputType.password}). For a reveal toggle and strength rules, use PasswordInput instead.
With Icon Click
An icon that does something, such as clearing the field or opening a picker, needs a click handler (onIconClick); without it the icon is drawn greyed out and ignores clicks, as in the other stories. Click the search icon to see the action.
With Prefix
A fixed part of the value that the user does not type sits in front of the input, inside the same border (children). Amount has a currency sign and no icon at its end (noIcon); Phone number has a country code and keeps its icon.
Right To Left
The same field under a right-to-left interface: the icon moves to the left end, the prefix to the right end, and the placeholder sits at the right edge. The direction comes from the theme's interfaceDirection (the Direction toolbar); the wrapper also carries dir="rtl" for the rules that read the DOM direction.
Css Customization
Every overridable variable set on one wrapper -- the variables are listed under CSS variables on this page.
Amount carries a prefix, for the prefix padding, and shows every other variable except the large icon padding; Large size is there for --input-block-icon-padding-lg, which only the large size reads. Hover and focus a field to see the hover and focus border colours.
Minimal example
type, value and onChange are the three that matter; tabIndex is the one that will catch
you out, because it defaults to -1.
import { useState } from "react";
import { InputBlock } from "@onlyoffice/apps-ui-kit/components/input-block";
import {
InputSize,
InputType,
} from "@onlyoffice/apps-ui-kit/components/text-input";
export function RoomName() {
const [name, setName] = useState("");
return (
<InputBlock
type={InputType.text}
size={InputSize.base}
value={name}
placeholder="Room name"
tabIndex={0}
scale
iconNode={<span aria-hidden="true">×</span>}
onIconClick={() => setName("")}
onChange={(event) => setName(event.target.value)}
/>
);
}
Props
Inherited from TextInputProps
Declared by components/text-input and accepted here too.
Plus 299 more props inherited from React.AriaAttributes, React.DOMAttributes, React.HTMLAttributes and React.InputHTMLAttributes, forwarded to the element.
Enums
Recipes
Disabled / read-only
isDisabled greys the group and removes the icon entirely, so a field whose icon carries
meaning changes shape when it is disabled. isReadOnly keeps the icon and the field's look, and
only stops the typing.
import { InputBlock } from "@onlyoffice/apps-ui-kit/components/input-block";
import {
InputSize,
InputType,
} from "@onlyoffice/apps-ui-kit/components/text-input";
export function PortalAddress({ address }: { address: string }) {
return (
<InputBlock
type={InputType.text}
size={InputSize.base}
value={address}
isReadOnly
tabIndex={0}
scale
iconNode={<span aria-hidden="true">⧉</span>}
onIconClick={() => navigator.clipboard.writeText(address)}
onChange={() => {}}
/>
);
}
Error
hasError and hasWarning recolour the group's border. Neither prints a message; that is
FieldContainer's job, or yours.
import { useState } from "react";
import { FieldContainer } from "@onlyoffice/apps-ui-kit/components/field-container";
import { InputBlock } from "@onlyoffice/apps-ui-kit/components/input-block";
import {
InputSize,
InputType,
} from "@onlyoffice/apps-ui-kit/components/text-input";
export function PortName() {
const [port, setPort] = useState("");
const problem = port && Number.isNaN(Number(port)) ? "Digits only" : "";
return (
<FieldContainer
isVertical
labelVisible
labelText="Port"
hasError={Boolean(problem)}
errorMessage={problem}
>
<InputBlock
type={InputType.text}
size={InputSize.base}
value={port}
hasError={Boolean(problem)}
tabIndex={0}
scale
noIcon
onChange={(event) => setPort(event.target.value)}
/>
</FieldContainer>
);
}
A prefix in front of the field
children are rendered before the input, inside the same border.
import { useState } from "react";
import { InputBlock } from "@onlyoffice/apps-ui-kit/components/input-block";
import { Text } from "@onlyoffice/apps-ui-kit/components/text";
import {
InputSize,
InputType,
} from "@onlyoffice/apps-ui-kit/components/text-input";
export function Subdomain() {
const [name, setName] = useState("");
return (
<InputBlock
type={InputType.text}
size={InputSize.base}
value={name}
tabIndex={0}
scale
noIcon
onChange={(event) => setName(event.target.value)}
>
<Text style={{ paddingInline: 8 }}>https://</Text>
</InputBlock>
);
}
Behaviour the types don't state
tabIndexdefaults to -1, so the field is out of the tab order unless you pass0. This is the single thing most likely to go wrong: the input looks right and the keyboard never reaches it.- The icon box is always rendered. Without
iconNameoriconNodeyou still get an empty box with its padding, and the field is narrower than it looks. PassnoIconfor a plain field. - An icon without
onIconClickis drawn in the disabled style. The innerIconButtonis givenisDisabledwhenever no handler is passed, so a purely decorative icon comes out greyed. isDisabledremoves the icon, rather than disabling it — the whole box is dropped from the markup, so the field changes width when it is disabled.- The border belongs to the group, not the input.
withBorder: falseis forced on the innerTextInput, which is whyhasError,hasWarningand the focus ring are drawn around everything including the prefix and the icon. maxLengthdefaults to 255 andautoCompleteto"off", both inherited from the same decision inTextInput.valueis required and the field is controlled, so it needs anonChangefrom the first render to be typeable at all.- It is not
scaled by default: the field hasTextInput's own base width until you passscale, even though the group around it is 100% wide. sizereaches the icon box too. Besides the text size and padding of the field, it sets the icon box to the field's line height, and atlargethe space after the icon grows from--input-block-icon-paddingto--input-block-icon-padding-lg. An unsetsizeleaves the box at its content's height.typeis passed straight to the inner<input>, sopasswordmasks the text andemailortelbring up the matching keyboard on a phone.idgoes on the inner<input>whileclassNameandstylego on the group, so ahtmlForpointing atidreaches the real field.childrenare wrapped in two nested boxes of their own, both inheriting the group's background, so a prefix sits flush inside the border.
CSS variables
The --text-input-* variables come from the shared input styles, so they mean what they mean
for TextInput; here the group reads them, because the inner field
draws no border of its own. Set them on the group or any ancestor.
Accessibility
- Pass
tabIndex={0}. The default of -1 takes a working text field out of the keyboard's reach, which is the most severe accessibility defect in this component. - The icon is an
IconButtoninside a clickable<div>; give it a name through your own markup, since nothing here labels it. - The field has no label of its own. Use
FieldContainer, or aLabelwithhtmlForpointing atid. hasErroronly changes a colour. Tie a message to the field witharia-describedbyand setaria-invalidyourself.- A prefix in
childrenis read before the field, which is usually right — but it is not part of the field's accessible name.
Test ids
Related
TextInput— the field inside, and where its props are documented.SearchInput— this component with search behaviour attached.PasswordInput— this component with a reveal toggle.