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"
>
×
</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;
☑️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;
🧾 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! 💻✨