FigUI
Components

Inputs

Inputs are the most tricky components in figma. The complexity is behind the great user experience.

There are some inputs in figui:

  • TextInput
  • ColorInput
  • NumericInput
  • and many combinations of them

Atanomy

By default, we export the most common input components like TextInput, ColorInput, NumericInput.

If you want to create a custom input component, you can use InputMultiRoot or InputRoot to wrap the primitive input components with Primitive suffix. Such as:

<InputMultiRoot>
  <TextInputPrimitive />
  <NumericInputPrimitive />
</InputMultiRoot>
<InputRoot>
  <NumericInputPrimitive iconLead={'X'} />
  <NumericInputPrimitive />
  <NumericInputPrimitive />
</InputRoot>

InputRoot

InputRoot is the root component of the input component. It is used to wrap the input components. It is basically a div with some default styles like rounded-md and border.

InputMultiRoot

InputMultiRoot is the root component like InputRoot. It is used to create a multi-input component.

Manual Installation

Make sure to copy the following files to your project:

input/input-utils.tsx
'use client';

import { cn } from '@/lib/utils';
import { Input as BaseInput } from '@base-ui-components/react';
import { createContext, useContext, useState } from 'react';

// types
interface BaseInputProps extends React.ComponentProps<typeof BaseInput> {
  iconLead?: React.ReactNode;
  iconTrail?: React.ReactNode;
}

type InputRootContextType = {
  isMiddleButtonDragging: boolean;
  setIsMiddleButtonDragging: (isMiddleButtonDragging: boolean) => void;
};

// context
const InputRootContext = createContext<InputRootContextType>({
  isMiddleButtonDragging: false,
  setIsMiddleButtonDragging: () => {},
});

// component
function InputRoot({ className, children }: BaseInputProps) {
  const [isMiddleButtonDragging, setIsMiddleButtonDragging] = useState(false);

  return (
    <InputRootContext.Provider
      value={{
        isMiddleButtonDragging,
        setIsMiddleButtonDragging,
      }}
    >
      <div
        className={cn(
          'placeholder:text-grey-400 text-black-800 bg-grey-100 typography-body-medium hover:ring-grey-200 dark:placeholder:text-grey-400 dark:bg-grey-700 dark:text-white-1000 dark:hover:ring-grey-600 flex h-6 w-full min-w-0 items-center rounded-md ring ring-transparent outline-none selection:bg-blue-500 focus-within:ring-blue-500 hover:focus-within:ring-blue-500 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-red-500/20 dark:focus-within:ring-blue-500',
          isMiddleButtonDragging &&
            'cursor-ew-resize !ring-blue-600 dark:!ring-blue-400',
          className,
        )}
      >
        {children}
      </div>
    </InputRootContext.Provider>
  );
}

function InputMultiRoot({
  children,
  className,
}: {
  children: React.ReactNode;
  className?: string;
}) {
  return (
    <InputRoot
      className={cn(
        'divide-white-1000 dark:divide-grey-800 flex divide-x [&>*:not(:last-child)]:pr-1',
        className,
      )}
    >
      {children}
    </InputRoot>
  );
}

// hook
function useInputRootContext(): InputRootContextType {
  return useContext(InputRootContext);
}

export { InputRoot, InputMultiRoot, type BaseInputProps, useInputRootContext };