🚀Reusable React Input Component for Tags & Colors – Built with TypeScript
JAKER HOSSAIN

JAKER HOSSAIN @jackfd120

About: Frontend Developer | Programming Enthusiast | Lifelong Learner Passionate frontend developer with 4 years of experience in React.js and Next.js. Always eager to learn and tackle new coding challenges.

Location:
Dhaka, Bangladesh
Joined:
Dec 19, 2024

🚀Reusable React Input Component for Tags & Colors – Built with TypeScript

Publish Date: Apr 20
3 0

Want to allow users to add and manage color names like tags—complete with a tiny preview bubble?

In this post, we’ll build a reusable, responsive, and accessible color picker input field using:

⚛️ React (TypeScript)
🎨 Tailwind CSS
💡 Keyboard support for Enter and Space to add colors
🏷️ Inline tag-style preview with removable color bubbles

Perfect for eCommerce dashboards, product editors, or any UI requiring dynamic color variants.

Pros
⚡ Fully reusable component – Just pass variants and setVariants state.
🎨 Color bubble preview – Visually shows added colors.
🧠 Prevents duplicate entries – Smart check before adding.
⌨️ Keyboard-friendly – Add colors via Enter or Space.
🔥 Styled with Tailwind CSS – Clean, responsive design out of the box.
🧩 TypeScript support – Strong typing for better DX.

⚠️ Cons
❌ No color validation (you can enter invalid color names like blaaaack)
🧪 Doesn’t use native — it's text-based
🗂️ Doesn’t support hex codes or color pickers (unless extended)

🛠️ Tip: You can improve it by adding color name validation or integrating a color picker library like react-colorful.

💡 Why You Should Use This
If you’re building a dashboard, admin panel, or storefront editor that needs customizable product variants (like colors), this component:

1- Saves time by being ready to drop into any project.
2- Makes UX delightful by allowing instant color tagging.
3- Promotes code reuse with a clean, state-managed approach.

"use client";
import React, { useState, KeyboardEvent } from "react";

interface Variant {
  variantName: string;
}

interface CustomInputColorPickUpProps {
  label?: string;
  variants: Variant[];
  setVariants: (variants: Variant[]) => void;
  placeholder?: string;
}

const CustomInputColorPickUp = ({
  label,
  variants,
  setVariants,
  placeholder = "Color Name",
}: CustomInputColorPickUpProps) => {
  const [colorInput, setColorInput] = useState<string>("");

  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
    if ((e.key === "Enter" || e.key === " ") && colorInput.trim()) {
      e.preventDefault();

      const normalizedInput = colorInput.trim().toLowerCase();

      const isDuplicate = variants?.some(
        (variant) => variant.variantName.toLowerCase() === normalizedInput
      );

      if (!isDuplicate) {
        setVariants([...variants, { variantName: colorInput.trim() }]);
      }

      setColorInput("");
    }
  };

  const handleRemove = (variantName: string) => {
    setVariants(variants.filter((v) => v.variantName !== variantName));
  };

  return (
    <div className="flex flex-col gap-2.5 w-full">
      {label && (
        <label
          htmlFor={label.toLowerCase()}
          className="text-black-50 text-base"
        >
          {label}
        </label>
      )}
      <div className="border border-black-10 rounded-md px-5 py-2 flex items-center gap-2 overflow-x-auto scrollbar-x-remove">
        {variants.map((variant, index) => (
          <div
            key={index}
            className="flex items-center rounded-sm bg-black-10 px-2"
          >
            <div
              style={{
                width: "13px",
                height: "13px",
                backgroundColor: variant.variantName,
                borderRadius: "50%",
                marginRight: "5px",
              }}
            />
            <span className="text-[15px]">
              {variant.variantName || "Not Specified"}
            </span>
            <button
              type="button"
              onClick={() => handleRemove(variant.variantName)}
              className="ml-2 text-lg"
            >
              &times;
            </button>
          </div>
        ))}
        <input
          type="text"
          value={colorInput}
          onChange={(e) => setColorInput(e.target.value)}
          onKeyDown={handleKeyDown}
          placeholder={placeholder}
          className="outline-none border-none w-full min-w-[150px]"
        />
      </div>
    </div>
  );
};

export default CustomInputColorPickUp;

Enter fullscreen mode Exit fullscreen mode

☑️Use Case:

"use client";
import React, { useState } from "react";
import CustomInputColorPickUp from "@/components/CustomInputColorPickUp";

const ProductColorInputSection = () => {
  const [colorVariants, setColorVariants] = useState([
    { variantName: "red" },
    { variantName: "blue" },
  ]);

  return (
    <div className="max-w-md mx-auto mt-10">
      <CustomInputColorPickUp
        label="Product Colors"
        variants={colorVariants}
        setVariants={setColorVariants}
        placeholder="Type a color and press Enter"
      />

      <pre className="mt-5 bg-gray-100 p-3 rounded text-sm">
        {JSON.stringify(colorVariants, null, 2)}
      </pre>
    </div>
  );
};

export default ProductColorInputSection;

Enter fullscreen mode Exit fullscreen mode

🧾 Conclusion (Note)
This reusable input component in React TypeScript is tailored for my specific use case — managing and displaying color variants dynamically. However, it’s fully customizable. Whether you want to use it for color tags, general tag inputs, or even more complex data entries, feel free to tweak the logic, styling, or structure based on your needs.

The goal is to give you a solid foundation for building flexible and reusable input components in modern React projects.

Happy coding! 💻✨

Comments 0 total

    Add comment