ProComponents, templates & AI tooling
HeroUI
27.7k

Input

Input 从 HeroUI v2 到 v3 的迁移指南。

完整的 API 参考、样式指南与高级示例请参阅 v3 Input 文档。本指南只关注从 HeroUI v2 的迁移。

关键变化:Input → TextField

v2: Input 是功能完备的组件,内置标签、描述、错误信息、校验、变体、颜色、尺寸等。

v3: Input 现在只是一个原语组件(仅是输入元素本身)。对于表单字段,请使用 TextField,它由 InputLabelDescriptionFieldError 等子组件组成。在 v2 中并没有独立的 TextField 或 InputGroup;单一的 Input 组件就负责标签、描述、起止内容与校验等所有职责。

何时使用 Input vs TextField

使用 TextField(大多数场景)

当你需要以下能力时,请使用 TextField

  • 标签
  • 描述
  • 错误信息
  • 校验
  • 表单集成

使用 Input(仅原语)

当你需要以下能力时,请使用 Input

  • 仅一个基础的 input 元素
  • 自定义的标签 / 错误处理
  • 与自定义表单组件集成

结构变化

在 v2 中,Input 是功能完备的组件,通过 prop 接收标签、描述、占位符等:

import { Input } from "@heroui/react";

export default function App() {
  return (
    <Input
      label="Email"
      placeholder="Enter your email"
      type="email"
    />
  );
}

在 v3 中,Input 是原语组件。对于带标签与校验的表单字段,请使用 TextField,将 InputLabelDescriptionFieldError 组合在一起:

import { TextField, Label, Input, FieldError } from "@heroui/react";

export default function App() {
  return (
    <TextField name="email" type="email">
      <Label>Email</Label>
      <Input placeholder="Enter your email" />
      <FieldError />
    </TextField>
  );
}

主要变化

1. 组件拆分

v2: 单一 Input 组件,承担所有职责
v3: 拆分为 Input(原语)与 TextField(复合组件)

2. Prop 变更

v2 propv3 位置说明
labelLabelTextField 内使用 Label 组件
descriptionDescriptionTextField 内使用 Description 组件
errorMessageFieldErrorTextField 内使用 FieldError 组件
variantInput从多种变体(flat、bordered、underlined、faded)改为 "primary"(默认,带阴影)与 "secondary"(弱化、无阴影,适合 Surface 场景)
colorsizeradius-已移除(请改用 Tailwind CSS)
fullWidthInputTextFieldInputTextField 上均仍支持 fullWidth 布尔属性
labelPlacement-通过 Label 的布局自行处理
startContentInputGroup.PrefixTextField 内使用 InputGroupInputGroup.Prefix
endContentInputGroup.SuffixTextField 内使用 InputGroupInputGroup.Suffix
isClearable-请通过按钮手动实现
isRequiredisInvalidTextField请设置在 TextField
validateTextFieldTextField 上使用 validate
classNames-改在各子组件上使用 className
onValueChangeInput改用 onChange 事件处理函数

迁移示例

表单校验

{/* With description */}
<Input
  description="We'll never share your email"
  label="Email"
  type="email"
/>

{/* With error message */}
<Input
  errorMessage="Please enter a valid email"
  isInvalid
  label="Email"
  type="email"
/>

{/* Required */}
<Input isRequired label="Email" type="email" />
import { Description } from "@heroui/react";

{/* With description */}
<TextField name="email" type="email">
  <Label>Email</Label>
  <Input />
  <Description>We'll never share your email</Description>
  <FieldError />
</TextField>

{/* With error message */}
<TextField
  isInvalid
  name="email"
  type="email"
  validate={(value) => {
    if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
      return "Please enter a valid email";
    }
    return null;
  }}
>
  <Label>Email</Label>
  <Input />
  <FieldError />
</TextField>

{/* Required */}
<TextField isRequired name="email" type="email">
  <Label>Email</Label>
  <Input />
  <FieldError />
</TextField>

Input 起止内容

对于带前缀或后缀内容的输入,请使用 InputGroup 复合组件:

<Input
  label="Price"
  startContent={<span>$</span>}
  type="number"
/>
import { InputGroup } from "@heroui/react";

<TextField name="price" type="number">
  <Label>Price</Label>
  <InputGroup>
    <InputGroup.Prefix>$</InputGroup.Prefix>
    <InputGroup.Input />
  </InputGroup>
  <FieldError />
</TextField>

InputGroup 为前缀 / 后缀内容提供合适的样式与布局。其中:

  • InputGroup.Prefix 表示位于输入之前的内容(取代 startContent
  • InputGroup.Suffix 表示位于输入之后的内容(取代 endContent
  • InputGroup.Input 表示输入元素本身

带清除按钮的 Input

<Input
  isClearable
  label="Email"
  onClear={() => console.log("cleared")}
  type="email"
/>
import { useState } from "react";
import { CloseButton } from "@heroui/react";

const [value, setValue] = useState("");

<TextField name="email" type="email">
  <Label>Email</Label>
  <div className="flex items-center">
    <Input value={value} onChange={(e) => setValue(e.target.value)} />
    {value && (
      <CloseButton
        aria-label="Clear"
        onPress={() => setValue("")}
      />
    )}
  </div>
  <FieldError />
</TextField>

受控 Input

import { useState } from "react";

const [value, setValue] = useState("");

<Input
  label="Email"
  onValueChange={setValue}
  type="email"
  value={value}
/>
import { useState } from "react";

const [value, setValue] = useState("");

<TextField name="email" type="email">
  <Label>Email</Label>
  <Input
    onChange={(e) => setValue(e.target.value)}
    value={value}
  />
  <FieldError />
</TextField>

总结

  1. 组件拆分:表单字段使用 TextField,仅原语输入使用 Input
  2. 标签必须显式声明:必须使用 Label 组件而不是 label prop
  3. 错误展示:必须使用 FieldError 组件而不是 errorMessage prop
  4. 描述:必须使用 Description 组件而不是 description prop
  5. 校验:将 validate 函数从 Input 移到 TextField
  6. 起止内容:在 TextField 内使用 InputGroupInputGroup.Prefix / InputGroup.Suffix
  7. 清除按钮:请手动实现 CloseButton
  8. 简化变体:v2 提供多种变体(flat、bordered、underlined、faded);v3 提供 "primary"(默认)与 "secondary"(适合 Surface)
  9. fullWidthfullWidth prop 在 InputTextField 上均可用
  10. 移除 color:请改用 Tailwind CSS 类
  11. 移除 size:请改用 Tailwind CSS 类
  12. 移除 radius:请改用 Tailwind CSS 类
  13. 移除 onValueChange:改用 onChange 事件处理函数
  14. 移除 classNames:改在各子组件上使用 className prop

本页目录