Fiber UI LogoFiberUI

Input

Displays a form input field or a component that looks like an input field.

Basic Usage

import { Input } from "@repo/ui/components/input";

interface Example1Props {}

export const Example1: React.FC<Example1Props> = ({}) => {
    return (
        <div className="sm:w-80">
            <Input type="email" placeholder="Email" />
        </div>
    );
};

File Input

import { Input } from "@repo/ui/components/input";
import { Label } from "@repo/ui/components/label";

interface Example2Props {}

export const Example2: React.FC<Example2Props> = ({}) => {
    return (
        <div className="flex flex-col gap-3 sm:w-80">
            <Label htmlFor="picture">Picture</Label>
            <Input id="picture" type="file" />
        </div>
    );
};

Disabled Input Field

import { Input } from "@repo/ui/components/input";

interface Example3Props {}

export const Example3: React.FC<Example3Props> = ({}) => {
    return (
        <div className="sm:w-80">
            <Input type="email" placeholder="Email disabled" disabled={true} />
        </div>
    );
};

Input with Label

import { Input } from "@repo/ui/components/input";
import { Label } from "@repo/ui/components/label";

interface Example4Props {}

export const Example4: React.FC<Example4Props> = ({}) => {
    return (
        <div className="flex flex-col gap-3 sm:w-80">
            <Label htmlFor="email">Email</Label>
            <Input type="email" id="email" placeholder="Email" />
        </div>
    );
};

Input with Button

import { Button } from "@repo/ui/components/button";
import { Input } from "@repo/ui/components/input";

interface Example5Props {}

export const Example5: React.FC<Example5Props> = ({}) => {
    return (
        <div className="flex gap-3 sm:w-96">
            <Input type="email" placeholder="Email" />
            <Button type="submit" className="rounded-lg">
                Subscribe
            </Button>
        </div>
    );
};

Component Code

"use client";

import { cn } from "@repo/ui/lib/utils";
import { ComponentProps, forwardRef, useRef } from "react";
import { AriaTextFieldProps, useFocusRing, useTextField } from "react-aria";
import { mergeProps } from "@react-aria/utils";

type InputComponentProps = ComponentProps<"input">;

interface InputProps
    extends InputComponentProps,
        Omit<AriaTextFieldProps, keyof InputComponentProps> {
    className?: string;
}

export const Input = forwardRef<HTMLInputElement, InputProps>(
    ({ className = "", type = "text", ...restProps }, ref) => {
        const localRef = useRef<HTMLInputElement>(null);

        const mergedRef = (node: HTMLInputElement | null) => {
            if (!node) {
                return;
            }
            localRef.current = node;

            if (typeof ref == "function") {
                ref(node);
            } else if (ref) {
                ref.current = node;
            }
        };

        const { inputProps } = useTextField(
            restProps as AriaTextFieldProps,
            localRef,
        );

        const { focusProps } = useFocusRing();

        return (
            <input
                {...mergeProps(inputProps, focusProps, restProps)}
                type={type}
                ref={mergedRef}
                className={cn(
                    "file:text-foreground placeholder:text-muted-foreground",
                    "selection:bg-primary selection:text-primary-foreground",
                    "dark:bg-input/30 border-input shadow-xs h-9 w-full min-w-0",
                    "rounded-md border bg-transparent px-3 py-1 text-base outline-none",
                    "transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium",
                    "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
                    "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
                    "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
                    className,
                )}
            />
        );
    },
);
Input.displayName = "Input";