Inputs

Input

A form input field.

Installation

  1. 1

    Install dependencies:

    npm install tailwind-variants @remixicon/react
  2. 2

    Add component:

    Copy and paste the code into your project’s component directory. Do not forget to update the import paths.
    // Tremor Input [v1.0.5]
    import React from "react"import { RiEyeFill, RiEyeOffFill, RiSearchLine } from "@remixicon/react"import { tv, type VariantProps } from "tailwind-variants"
    import { cx, focusInput, focusRing, hasErrorInput } from "@/lib/utils"
    const inputStyles = tv({  base: [    // base    "relative block w-full appearance-none rounded-md border px-2.5 py-2 shadow-sm outline-none transition sm:text-sm",    // border color    "border-gray-300 dark:border-gray-800",    // text color    "text-gray-900 dark:text-gray-50",    // placeholder color    "placeholder-gray-400 dark:placeholder-gray-500",    // background color    "bg-white dark:bg-gray-950",    // disabled    "disabled:border-gray-300 disabled:bg-gray-100 disabled:text-gray-400",    "disabled:dark:border-gray-700 disabled:dark:bg-gray-800 disabled:dark:text-gray-500",    // file    [      "file:-my-2 file:-ml-2.5 file:cursor-pointer file:rounded-l-[5px] file:rounded-r-none file:border-0 file:px-3 file:py-2 file:outline-none focus:outline-none disabled:pointer-events-none file:disabled:pointer-events-none",      "file:border-solid file:border-gray-300 file:bg-gray-50 file:text-gray-500 file:hover:bg-gray-100 file:dark:border-gray-800 file:dark:bg-gray-950 file:hover:dark:bg-gray-900/20 file:disabled:dark:border-gray-700",      "file:[border-inline-end-width:1px] file:[margin-inline-end:0.75rem]",      "file:disabled:bg-gray-100 file:disabled:text-gray-500 file:disabled:dark:bg-gray-800",    ],    // focus    focusInput,    // invalid (optional)    // "aria-[invalid=true]:dark:ring-red-400/20 aria-[invalid=true]:ring-2 aria-[invalid=true]:ring-red-200 aria-[invalid=true]:border-red-500 invalid:ring-2 invalid:ring-red-200 invalid:border-red-500"    // remove search cancel button (optional)    "[&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden",  ],  variants: {    hasError: {      true: hasErrorInput,    },    // number input    enableStepper: {      false:        "[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",    },  },})
    interface InputProps  extends React.InputHTMLAttributes<HTMLInputElement>,    VariantProps<typeof inputStyles> {  inputClassName?: string}
    const Input = React.forwardRef<HTMLInputElement, InputProps>(  (    {      className,      inputClassName,      hasError,      enableStepper = true,      type,      ...props    }: InputProps,    forwardedRef,  ) => {    const [typeState, setTypeState] = React.useState(type)
        const isPassword = type === "password"    const isSearch = type === "search"
        return (      <div className={cx("relative w-full", className)} tremor-id="tremor-raw">        <input          ref={forwardedRef}          type={isPassword ? typeState : type}          className={cx(            inputStyles({ hasError, enableStepper }),            {              "pl-8": isSearch,              "pr-10": isPassword,            },            inputClassName,          )}          {...props}        />        {isSearch && (          <div            className={cx(              // base              "pointer-events-none absolute bottom-0 left-2 flex h-full items-center justify-center",              // text color              "text-gray-400 dark:text-gray-600",            )}          >            <RiSearchLine              className="size-[1.125rem] shrink-0"              aria-hidden="true"            />          </div>        )}        {isPassword && (          <div            className={cx(              "absolute bottom-0 right-0 flex h-full items-center justify-center px-3",            )}          >            <button              aria-label="Change password visibility"              className={cx(                // base                "h-fit w-fit rounded-sm outline-none transition-all",                // text                "text-gray-400 dark:text-gray-600",                // hover                "hover:text-gray-500 hover:dark:text-gray-500",                focusRing,              )}              type="button"              onClick={() => {                setTypeState(typeState === "password" ? "text" : "password")              }}            >              <span className="sr-only">                {typeState === "password" ? "Show password" : "Hide password"}              </span>              {typeState === "password" ? (                <RiEyeFill aria-hidden="true" className="size-5 shrink-0" />              ) : (                <RiEyeOffFill aria-hidden="true" className="size-5 shrink-0" />              )}            </button>          </div>        )}      </div>    )  },)
    Input.displayName = "Input"
    export { Input, inputStyles, type InputProps }
    

Example: Input with label

Example: Input with type "password"

Clicking on the icon switches the input type from password to text.

Example: Input with type "number"

Type number activates the prop enableStepper by default.

Example: Input with type "file"

You are only allowed to upload .CSV, .XLSX or .XLS files.

Example: Input with disabled state

Example: Input with error

To style the Input for an error state, use the hasError prop.

Example: Controlled input

Search term:

API Reference: Input

This component is based on the Input element and supports all of its props.

className
string
Set a className to the wrapping div of the Input.
inputClassName
string
Set a className to the nested input.
hasError
boolean
Style for erroneous input.

Default: false