Kaapi UI
← Back to Design System

Input

Inputs with sizes, icons, tooltip, addons and form integration.

Sizes

<Input size="sm" placeholder="Size sm" />
<Input size="md" placeholder="Size md" />

Input with icons

<Input 
  type="email" 
  placeholder="email" 
  leftIcon={Mail01}
/>
<Input 
  type="email" 
  placeholder="email" 
  rightIcon={Mail01}
/>

Input with tooltip


<Input 
    type="text" 
    placeholder="Input with tooltip" 
    tooltip="Input with tooltip"
/>

Payment Input

Specialized input for credit card numbers with automatic card type detection.

<PaymentInput placeholder="Card number" />
<PaymentInput placeholder="Card number" size="md" />

OTP Input

Verification code input components built for modern applications and websites.

0
0
0
0
<InputOTP size="sm" slots={4} maxLength={4} />
0
0
0
0
0
0
<InputOTP slots={6} maxLength={4} separator />

InputGroup

https://
.com
<InputGroup
  type="text"
  leftAddon={<InputAffix>https://</InputAffix>}
  rightAddon={<InputAffix>.com</InputAffix>}
  size="sm"
  tooltip="This is a tooltip"
/>
Copy

<InputGroup
    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
        setValue(e.target.value)
    }
    type="text"
    rightAddon={
        <InputAffix
            onClick={() => copy(value || "Copied")}
            className={cn(
                "flex items-center gap-1 bg-primary text-secondary hover:bg-primary_hover hover:text-secondary_hover cursor-pointer"
            )}
        >
            {copied ? (
                <Check className="size-4" />
            ) : (
                <Copy01 className="size-4" />
            )}
            Copy
        </InputAffix>
    }
    size="sm"
    tooltip="This is a tooltip"
/>
<InputGroup
  leftAddon={
    <NativeSelect
      aria-label="Country"
      options={[
        { value: "US", label: "US" },
        { value: "CA", label: "CA" },
        { value: "EU", label: "EU" },
      ]}
    />
  }
  type="tel"
  placeholder="+1 (555) 000-0000"
/>

$

<InputGroup
  prefix="$"
  rightAddon={
    <NativeSelect
      aria-label="Currency"
      options={[
        { value: "USD", label: "US" },
        { value: "CAD", label: "CA" },
        { value: "EUR", label: "EU" },
      ]}
    />
  }
  type="tel"
  placeholder="1,000.00"
/>

Form

This is your public display name.

This is your login email.

Enter your credit card number.

0
0
0
0
0
0

Entrez le code


import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

const formSchema = z.object({
  username: z.string().min(2, { message: "Username must be at least 2 characters." }),
  email: z.string().email(),
  cardNumber: z.string().min(13, { message: "Card number must be at least 13 digits." }),
  code: z.string().min(6, {
        message: "Your one-time password must be 6 characters.",
    }),
});

const form = useForm<z.infer<typeof formSchema>>({
  resolver: zodResolver(formSchema),
  defaultValues: { username: "", email: "", cardNumber: "", code: "" },
});

function onSubmit(data: z.infer<typeof formSchema>) {
  toast("You submitted the following values", {
    description: (
      <pre className="mt-2 w-[320px] rounded-md bg-neutral-950 p-4">
        <code className="text-white">{JSON.stringify(data, null, 2)}</code>
      </pre>
    ),
  });
}

<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
    <InputForm
      control={form.control}
      name="username"
      label="Username"
      placeholder="shadcn"
      description="This is your public display name."
      leftIcon={User02}
      tooltip="This is a tooltip"
      isRequired
    />
    <InputForm
      control={form.control}
      type="email"
      name="email"
      label="Email"
      placeholder="Your email"
      description="This is your login email."
      leftIcon={Mail02}
      tooltip="This is a tooltip"
      isRequired
    />
    <PaymentInputForm
      control={form.control}
      name="cardNumber"
      label="Card Number"
      placeholder="1234 5678 9012 3456"
      description="Enter your credit card number."
      isRequired
    />
    <InputOTPForm
        name="code"
        control={form.control}
        label="Code de vérification"
        description="Entrez le code"
        slots={6}
        separator={true}
        isRequired
    />
    <Button type="submit">Submit</Button>
  </form>
</Form>

API Reference

INPUT (Props additionnels à Input HTML)

PropsTypeDefaultDescription
size?"sm" | "md""sm"Controls input padding.
leftIcon?React.FC<SVGProps>Icon displayed on the left.
rightIcon?React.F<SVGProps>Icon displayed on the right.
tooltip?stringTooltip message on hover/focus.
inputWrapperClassName?stringStyles for the outer wrapper.
inputClassName?stringStyles for the native input.
iconClassName?stringStyles for icons.
tooltipClassName?stringStyles for tooltip content.

INPUT GROUP

PropsTypeDefaultDescription
leftAddon?ReactNodeAddon before input.
rightAddon?ReactNodeAddon after input.
size?"sm" | "md""sm"Controls input padding.
tooltip?stringTooltip message on hover/focus.
inputGroupClassName?stringStyles for the group container.
inputWrapperClassName?stringStyles passed to the inner Input wrapper.
inputClassName?stringExtra classes applied to the native input.

INPUT AFFIX

PropsTypeDefaultDescription
isDisabled?booleanfalseVisual disabled state for the addon.
childrenReactNodeAddon content.

INPUT FORM

PropsTypeDefaultDescription
controlControl<TFieldValues>React Hook Form control instance.
nameFieldPath<TFieldValues>Field name registered in the form schema.
label?ReactNode | stringField label displayed above input.
description?ReactNode | stringHelper text under the input.
isRequired?booleanfalse Adds "required" indicator on label.
…inputPropsAll Input props (except name)Pass-through to the inner Input.

PAYMENT INPUT FORM

PropsTypeDefaultDescription
controlControl<TFieldValues>React Hook Form control instance.
nameFieldPath<TFieldValues>Field name registered in the form schema.
label?ReactNode | stringField label displayed above input.
description?ReactNode | stringHelper text under the input.
isRequired?booleanfalseAdds "required" indicator on label.
tooltip?stringTooltip message on hover/focus.
tooltipClassName?stringStyles for tooltip content.

INPUT OTP

PropsTypeDefaultDescription
slots?number6Number of input boxes to render.
size?"sm" | "md" | "lg""md"Controls the size of each input slot.
separator?boolean | number[]falseIf true, adds a separator in the middle (if slots are even). Can be an array of positions (e.g., [3, 6]).
maxLength?numberslotsMaximum characters allowed. Defaults to slots.
disabled?booleanfalseDisables user input.
containerClassName?stringExtra classes for the flex container wrapping the slots.
inputClassName?stringExtra classes for the underlying invisible<input> element (used for keyboard and paste handling).

INPUT OTP FORM

PropsTypeDefaultDescription
controlControl<TFieldValues>React Hook Form control instance.
nameFieldPath<TFieldValues>Field name registered in the form schema.
label?ReactNode | stringField label displayed above input.
description?ReactNode | stringHelper text under the input.
isRequired?booleanfalseAdds required indicator on label.
slots?number6Number of input boxes to render.
size?"sm" | "md" | "lg""md"Controls the size of each input slot.
maxLength?numberslotsMaximum characters allowed.
renderSlots?(slots: number) => React.ReactNodeCustom rendering function for slots and separators.
…otpPropsAll InputOTP props (except value and onChange).Props passed to the inner InputOTP component.