Leading icon
Input field with a leading icon adornment to visually hint at the expected input type.
import { HvIconContainer, HvInput } from "@hitachivantara/uikit-react-core"; export default function Demo() { return ( <HvInput label="Leading Icon" className="w-300px" placeholder="Placeholder text..." startAdornment={ <HvIconContainer> <div className="i-ph-user" /> </HvIconContainer> } /> ); }
Copy button
Input field with a trailing copy-to-clipboard button adornment.
import { useRef, useState } from "react"; import { HvAdornment, HvInput } from "@hitachivantara/uikit-react-core"; import { Copy, Success } from "@hitachivantara/uikit-react-icons"; export default function Demo() { const [copied, setCopied] = useState(false); const inputRef = useRef<HTMLInputElement>(null); const handleCopy = () => { navigator.clipboard.writeText(inputRef.current?.value || ""); setCopied(true); setTimeout(() => { setCopied(false); }, 1000); }; return ( <HvInput label="Copy to clipboard" ref={inputRef} className="w-300px" defaultValue="Copy this text" endAdornment={ <HvAdornment icon={ <> <Success color="positive" className={`transition-all ${copied ? "scale-100 opacity-100" : "scale-0 opacity-0"}`} /> <Copy className={`absolute transition-all ${copied ? "scale-0 opacity-0" : "scale-100 opacity-100"}`} /> </> } onClick={handleCopy} /> } /> ); }
Label left side
Input field with the label positioned to the left rather than above the input.
import { HvFormElement, HvInput, HvLabel, } from "@hitachivantara/uikit-react-core"; export default function Demo() { return ( <HvFormElement required className="flex gap-xs"> <HvLabel label="Name" htmlFor="username-input" className="h-fit mt-5px" /> <HvInput id="username" className="w-300px" required placeholder="My label is on the side..." /> </HvFormElement> ); }
Dropdown prefix
Input field with a dropdown selector as a prefix, useful for type or category selection.
import { useState } from "react"; import { HvInput, HvOption, HvOverflowTooltip, HvSelect, HvTypography, } from "@hitachivantara/uikit-react-core"; export default function Demo() { const [phoneNumber, setPhoneNumber] = useState(""); const [value, setValue] = useState<string | null>(null); return ( <HvInput label="Dropdown prefix" className="w-300px" onChange={(_, val) => setPhoneNumber(val)} placeholder="Type phone number..." value={phoneNumber} startAdornment={ <HvSelect enablePortal value={value} variableWidth onChange={(_, val) => setValue(val)} classes={{ panel: "w-200px max-h-260px", select: "min-w-95px! border-0 border-r-1px rounded-none", }} placeholder="Select..." renderValue={(option) => option?.value ?? ""} > {countries.map((country) => ( <HvOption key={country.code} value={`${country.flag} ${country.code}`} > <div className="flex justify-between items-center"> <HvOverflowTooltip data={`${country.flag} ${country.label}`} /> <HvTypography variant="captionLabel" className="color-text-subtle" > {country.code} </HvTypography> </div> </HvOption> ))} </HvSelect> } /> ); } const countries = [ { flag: "🇦🇺", code: "+61", label: "Australia" }, { flag: "🇧🇷", code: "+55", label: "Brazil" }, { flag: "🇨🇦", code: "+1", label: "Canada" }, { flag: "🇨🇱", code: "+56", label: "Chile" }, { flag: "🇪🇬", code: "+20", label: "Egypt" }, { flag: "🇫🇷", code: "+33", label: "France" }, { flag: "🇩🇪", code: "+49", label: "Germany" }, { flag: "🇮🇳", code: "+91", label: "India" }, { flag: "🇮🇹", code: "+39", label: "Italy" }, { flag: "🇯🇵", code: "+81", label: "Japan" }, { flag: "🇲🇽", code: "+52", label: "Mexico" }, { flag: "🇳🇿", code: "+64", label: "New Zealand" }, { flag: "🇳🇴", code: "+47", label: "Norway" }, { flag: "🇵🇹", code: "+351", label: "Portugal" }, { flag: "🇸🇬", code: "+65", label: "Singapore" }, { flag: "🇿🇦", code: "+27", label: "South Africa" }, { flag: "🇰🇷", code: "+82", label: "South Korea" }, { flag: "🇪🇸", code: "+34", label: "Spain" }, { flag: "🇸🇪", code: "+46", label: "Sweden" }, { flag: "🇨🇭", code: "+41", label: "Switzerland" }, { flag: "🇦🇪", code: "+971", label: "United Arab Emirates" }, { flag: "🇬🇧", code: "+44", label: "United Kingdom" }, { flag: "🇺🇸", code: "+1", label: "United States" }, ];
Dropdown suffix
Input field with a dropdown selector as a suffix, useful for unit or format selection.
import { useState } from "react"; import { HvInput, HvSelect } from "@hitachivantara/uikit-react-core"; export default function Demo() { const [email, setEmail] = useState(""); const [role, setRole] = useState<string | null>(null); return ( <HvInput label="Dropdown suffix" className="w-300px" onChange={(_, value) => { setEmail(value); }} value={email} placeholder="Type user email..." endAdornment={ <HvSelect enablePortal value={role} onChange={(_, val) => setRole(val)} classes={{ root: "w-140px! border-l-1! border-l-border! rounded-none!", panel: "w-150px! p-sm", select: "border-none! rounded-none! bg-bgContainer! hover:bg-primaryDimmed", popper: "m-l-[-10px]!", }} options={roles} placeholder="Select..." /> } /> ); } const roles = [ { label: "Admin", value: "admin" }, { label: "Editor", value: "editor" }, { label: "Reviewer", value: "reviewer" }, { label: "View only", value: "view" }, ];
Dropdown with treeview
Input field combined with a treeview dropdown panel for hierarchical value selection.
Select a file or folder...
import { forwardRef, useState } from "react"; import { HvBaseDropdown, HvIconContainer, HvLabel, HvPanel, HvTreeItem, HvTreeView, type HvTreeItemProps, } from "@hitachivantara/uikit-react-core"; export default function Demo() { const [file, setFile] = useState(""); const [open, setOpen] = useState(false); return ( <> <HvLabel>Dropdown with treeview</HvLabel> <HvBaseDropdown expanded={open} className="w-300px" placeholder={file || "Select a file or folder..."} adornment={ <HvIconContainer> <div className="i-ph-list-bullets" /> </HvIconContainer> } classes={{ arrowContainer: "flex w-32px h-full items-center justify-center", }} onToggle={(_evt, s) => setOpen(s)} > <HvPanel> <HvTreeView multiSelect={false} aria-label="file system navigator" onNodeSelect={(_, val) => { console.log(val); setFile(val); }} > {renderItem(treeDataObject)} </HvTreeView> </HvPanel> </HvBaseDropdown> </> ); } type TreeData = { id: string; label: string; children?: TreeData[] }; const treeDataObject = { id: "user", label: "User", children: [ { id: "Applications", label: "Applications", children: [ { id: "Code", label: "Code.app" }, { id: "Chrome", label: "Chrome.app" }, { id: "Firefox", label: "Firefox.app" }, ], }, { id: "Documents", label: "Documents", children: [{ id: "secret", label: "secret.txt" }], }, { id: "git", label: "git", children: [ { id: "uikit-react", label: "uikit-react", children: [{ id: "uikit-pkg", label: "package.json" }], }, { id: "app-shell", label: "app-shell", children: [{ id: "as-pkg", label: "package.json" }], }, ], }, ], } satisfies TreeData; const SimpleTreeItem = forwardRef<HTMLLIElement, HvTreeItemProps>( function SimpleTreeItem(props, ref) { const { children, nodeId, label, ...others } = props; const Icon = children ? () => ( <HvIconContainer> <div className="i-ph-folder" /> </HvIconContainer> ) : () => ( <HvIconContainer> <div className="i-ph-file" /> </HvIconContainer> ); return ( <HvTreeItem ref={ref} nodeId={nodeId} label={ <div className="flex items-center"> <Icon /> <span style={{ flex: 1 }}>{label}</span> </div> } {...others} > {children} </HvTreeItem> ); }, ); /** Render tree view items */ const renderItem = ({ id, label, children }: TreeData) => ( <SimpleTreeItem key={id} nodeId={id} label={label}> {children?.map(renderItem)} </SimpleTreeItem> );
Tags dropdown
Multi-select input where chosen values appear as removable tags, with a dropdown for selection.
Select colors from the dropdown...
import { useRef, useState } from "react"; import { flushSync } from "react-dom"; import { HvBaseDropdown, HvLabel, HvPanel, HvTag, } from "@hitachivantara/uikit-react-core"; const colors = [ "Blue", "Red", "Green", "Yellow", "Purple", "White", "Black", "Orange", "Pink", "Brown", "Gray", "Cyan", "Magenta", "Lime", "Teal", "Lavender", ]; export default function Demo() { const containerRef = useRef<HTMLDivElement>(null); const focusTarget = useRef<HTMLDivElement>(null); const [selectedColors, setSelectedColors] = useState<string[]>([]); const scrollToEnd = () => { containerRef.current?.scrollTo({ left: containerRef.current.scrollWidth, behavior: "smooth", }); }; const handleAddColor = (color: string) => { flushSync(() => { setSelectedColors((prev) => [...prev.filter((c) => c !== color), color]); }); scrollToEnd(); }; const handleRemoveColor = (color: string) => { flushSync(() => { setSelectedColors((prev) => prev.filter((c) => c !== color)); }); scrollToEnd(); }; return ( <div className="w-300px"> <HvLabel label="Dropdown with tags on header" id="tags-dropdown" showGutter /> <HvBaseDropdown aria-labelledby="tags-dropdown" onContainerCreation={() => focusTarget.current?.focus()} placeholder={ selectedColors.length ? ( <div className="flex gap-xs overflow-scroll px-1px h-full items-center" ref={containerRef} > {selectedColors.map((color) => ( <HvTag key={color} label={color} onDelete={() => handleRemoveColor(color)} /> ))} </div> ) : ( "Select colors from the dropdown..." ) } > <div ref={focusTarget} tabIndex={-1} /> <HvPanel className="flex gap-xs flex-wrap"> {colors .filter((color) => !selectedColors.includes(color)) .map((color) => ( <HvTag key={color} label={color} onClick={() => handleAddColor(color)} /> ))} </HvPanel> </HvBaseDropdown> </div> ); }
Tags input dropdown
Combined free-text input and dropdown panel that produces a list of tag values.
import { useEffect, useRef, useState } from "react"; import { HvAdornment, HvDropdownPanel, HvTag, HvTagsInput, HvTypography, } from "@hitachivantara/uikit-react-core"; import { DropDownXS } from "@hitachivantara/uikit-react-icons"; const colors = [ "Blue", "Red", "Green", "Yellow", "Purple", "White", "Black", "Orange", "Pink", "Brown", "Gray", "Cyan", "Magenta", "Lime", "Teal", "Lavender", ]; const lastUsed = ["Blue", "Red", "Green"]; export default function Demo() { const containerRef = useRef<HTMLDivElement>(null); const [selectedColors, setSelectedColors] = useState<string[]>([]); const [open, setOpen] = useState(false); useEffect(() => { if (containerRef.current) { containerRef.current.scrollLeft = containerRef.current.scrollWidth; } }, [selectedColors]); const handleAddColor = (color: string) => { setSelectedColors((prev) => { const newColors = prev.filter((c) => c !== color); return newColors.length ? [...newColors, color] : [color]; }); }; const handleRemoveColor = (color: string) => { setSelectedColors((prev) => prev.filter((c) => c !== color)); }; return ( <div className="w-300px"> <HvTagsInput label="TagsInput with Dropdown" placeholder="Enter tags or select from the dropdown..." ref={containerRef} onChange={(event, value) => { setSelectedColors( value.map((v) => (typeof v === "string" ? v : (v.label as string))), ); }} onDelete={(_, value) => { handleRemoveColor(value as string); }} value={selectedColors} commitTagOn={["Comma", "Enter"]} endAdornment={ <HvAdornment tabIndex={0} icon={<DropDownXS rotate={open} size="xs" />} onClick={() => setOpen((o) => !o)} /> } classes={{ tagsList: "h-32px pr-0! overflow-hidden", }} /> <HvDropdownPanel open={open} placement="bottom-start" anchorEl={containerRef.current} onClickAway={() => setOpen(false)} classes={{ panel: "grid gap-xs p-sm w-300px" }} > <HvTypography variant="caption1">Last Used:</HvTypography> <div className="flex gap-xs"> {lastUsed.map((color, idx) => ( <HvTag autoFocus={idx === 0} key={color} label={color} onClick={() => handleAddColor(color)} /> ))} </div> <HvTypography variant="caption1">More colors:</HvTypography> <div className="flex flex-wrap gap-xs"> {colors .filter( (color) => !selectedColors.includes(color) && !lastUsed.includes(color), ) .map((color) => ( <HvTag key={color} label={color} onClick={() => handleAddColor(color)} /> ))} </div> </HvDropdownPanel> </div> ); }
Inline editable text
A read-only text value that switches to an editable input on click.
import { useRef, useState } from "react"; import { HvBaseInput, HvButton, HvIconButton, HvTypography, } from "@hitachivantara/uikit-react-core"; import { Check, Close, Edit } from "@hitachivantara/uikit-react-icons"; export default function Demo() { const [editing, setEditing] = useState(false); const inputRef = useRef<HTMLInputElement>(null); const [cachedValue, setCachedValue] = useState("John Doe"); return editing ? ( <form className="flex items-center gap-xs w-260px" onSubmit={(evt) => { evt.preventDefault(); setCachedValue(inputRef.current?.value || ""); setEditing(false); }} onReset={() => setEditing(false)} > <HvBaseInput ref={inputRef} autoFocus defaultValue={cachedValue} /> <HvIconButton type="reset" size="xs" variant="negative" title="Cancel"> <Close /> </HvIconButton> <HvIconButton type="submit" size="xs" variant="positive" title="Save"> <Check /> </HvIconButton> </form> ) : ( <div className="flex items-center gap-xs w-260px"> <span className="capitalize">Name:</span> <HvTypography variant="label">{cachedValue}</HvTypography> <HvButton icon title="Edit" onClick={() => setEditing(true)}> <Edit /> </HvButton> </div> ); }