DEV Community

Cover image for React - Building Chip and Chip Input Component from Scratch
Ayesha Munir
Ayesha Munir

Posted on

React - Building Chip and Chip Input Component from Scratch

In this blog post, we will walk through creating a Chip and Chip Input component in React. These components are commonly used in modern web applications for tagging, filtering, and input selection.

Chip Component
The Chip component is a small, interactive element that represents an input, attribute, or action. It is a versatile component that can be used in a variety of ways.

Here is the code for our Chip component:

import React from 'react'
import PropTypes from 'prop-types'
import { Label } from './ui/label'
import { IconX } from '@tabler/icons-react'
import { getProfileIcon } from '@/lib/helpers/common'

const imageProps = {
    width: 20,
    height: 20,
}

const avatarStyles = 'w-[20px] h-[20px]'

const Chip = ({ label, color, containerClass, labelClass, isDelete, isImage, onDelete, chipStyles, chipImage }) => {
    return (
        <div
            className={`px-2 ${color} rounded justify-center items-center gap-1 inline-flex ${chipStyles} ${containerClass}`}
        >
            <div className="justify-center items-center gap-1 flex">
                {isImage && getProfileIcon(chipImage, imageProps, label, avatarStyles, 'text-sm')}
                <Label className={`text-zinc-600 text-xs font-medium ${labelClass}`}>{label}</Label>
            </div>

            {isDelete && (
                <button onClick={onDelete}>
                    <IconX size={16} stroke={2} color={'red'} />
                </button>
            )}
        </div>
    )
}

Chip.propTypes = {
    label: PropTypes.string.isRequired,
    isDelete: PropTypes.bool,
    onDelete: PropTypes.func,
    color: PropTypes.string,
    chipStyles: PropTypes.string,
    isImage: PropTypes.bool,
    chipImage: PropTypes.string,
    containerClass: PropTypes.string,
    labelClass: PropTypes.string,
}

export default Chip
Enter fullscreen mode Exit fullscreen mode

On the Right side you can see chips with different titles and you can also make it profile tags and show user images and avatars into it!
Chip for tags


Chip Input Component
The Chip Input component is a text input that can contain multiple "chips" as input. It is useful for tagging, multi-select, or input suggestions.

Here is the code for our Chip Input component:

import React, { useState, useRef } from 'react'
import Chip from './chip'
import PropTypes from 'prop-types'

function ChipsInput({ chips, setChips, chipColor }) {
    const [inputValue, setInputValue] = useState('')
    const [suggestions, setSuggestions] = useState(['Person', 'User'])
    const [removedSuggestions, setRemovedSuggestions] = useState([])
    const inputRef = useRef()

    const handleInputChange = (event) => {
        setInputValue(event.target.value)
    }

    const handleDeleteChip = (chipToDelete) => {
        setChips(chips.filter((chip) => chip !== chipToDelete))
        if (removedSuggestions.includes(chipToDelete)) {
            setSuggestions((prevSuggestions) => [...prevSuggestions, chipToDelete])
            setRemovedSuggestions((prevRemoved) => prevRemoved.filter((item) => item !== chipToDelete))
        }
        inputRef.current.focus()
    }

    const handleInputKeyDown = (event) => {
        if (event.key === 'Enter') {
            event.preventDefault()
            const newChip = inputValue.trim()
            if (newChip !== '') {
                setChips([...chips, newChip])
                setInputValue('')
            }
        } else if (event.key === 'Backspace' && inputValue === '') {
            const lastChip = chips[chips.length - 1]
            handleDeleteChip(lastChip)
        }
    }

    const handleSuggestionClick = (suggestion) => {
        setChips([...chips, suggestion])
        setSuggestions((prevSuggestions) => prevSuggestions.filter((prevSuggestion) => prevSuggestion !== suggestion))
        setRemovedSuggestions((prevRemoved) => [...prevRemoved, suggestion])
        setInputValue('')
    }

    return (
        <div className="relative">
            <div className="flex flex-wrap gap-2 min-h-10 items-center bg-fieldsBg p-2 rounded">
                {chips.map((chip) => (
                    <Chip
                        key={chip}
                        containerClass="h-[26px]"
                        label={chip}
                        isDelete={true}
                        color={chipColor}
                        onDelete={() => handleDeleteChip(chip)}
                    />
                ))}
                <input
                    ref={inputRef}
                    type="text"
                    value={inputValue}
                    onChange={handleInputChange}
                    onKeyDown={handleInputKeyDown}
                    className="flex-grow bg-fieldsBg text-sm outline-none"
                />
            </div>
            {!!suggestions.length && inputValue && (
                <div className="absolute left-0 mt-1 w-full bg-white rounded shadow z-10">
                    {!!suggestions.filter((suggestion) => suggestion.toLowerCase().includes(inputValue.toLowerCase())).length &&
                        inputValue && (
                            <div className="absolute left-0 mt-1 w-full bg-white border rounded shadow z-10">
                                {suggestions
                                    .filter((suggestion) => suggestion.toLowerCase().includes(inputValue.toLowerCase()))
                                    .map((suggestion) => (
                                        <button
                                            key={suggestion}
                                            onClick={() => handleSuggestionClick(suggestion)}
                                            className="p-2 hover:bg-gray-200 cursor-pointer w-full text-left"
                                        >
                                            {suggestion}
                                        </button>
                                    ))}
                            </div>
                        )}
                </div>
            )}
        </div>
    )
}

ChipsInput.propTypes = {
    chips: PropTypes.arrayOf(PropTypes.string).isRequired,
    setChips: PropTypes.func.isRequired,
    chipColor: PropTypes.string.isRequired,
}

export default ChipsInput
Enter fullscreen mode Exit fullscreen mode

Here you can see two chip inputs with different background chip color and cross icon to remove it and can also remove it through backspaceπŸ™Œ
Chip input

That's it for now, folks! πŸŽ‰ I hope you found this post useful and it helps you in your coding journey. Remember, practice is key when it comes to mastering programming. Keep coding and stay curious! πŸ’»πŸš€

If you have any questions or feedback, feel free to leave a comment below. I'd love to hear from you! 😊

Thank you for reading! If you enjoyed this post, please consider giving it a 🧑 or πŸ¦„ to help more people find it.

Happy coding! 🎈

🌐 Get in touch: Ayesha Munir
πŸ‘₯ Connect: Linkedin | Facebook | Instagram


Top comments (0)