A tag input component created using Shadcn UI



Shadcn Tag Input is a tag input component implementation of Shadcn’s input component. It’s customizable, but styled by default (Shadcn’s default styling).

View Demo View Github

Setup

Run the shadcn-ui init command to setup your project:

npx shadcn-ui@latest init

Run the shadcn-ui add command to add the tag input component to your project:

npx shadcn-ui@latest add input

Copy and paste the folowing code into a new file:

import React from 'react';
import { Input } from './ui/input';
import { Button } from './ui/button';
import { X } from 'lucide-react';
import { cn } from '@/lib/utils';

interface TagInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  placeholder?: string;
  tags: string[];
  setTags: React.Dispatch<React.SetStateAction<string[]>>;
}

const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>((props, ref) => {

    const { placeholder, tags, setTags, className } = props;

    const [inputValue, setInputValue] = React.useState('');
    const inputRef = React.useRef<HTMLInputElement>(null);

    const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setInputValue(e.target.value);
    };

    const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
        
        if (e.key === 'Enter' || e.key === ',') {
            e.preventDefault();
            const newTag = inputValue.trim();
            if (newTag && !tags.includes(newTag)) {
                setTags([...tags, newTag]);
            }
            setInputValue('');
        }
    };

    const removeTag = (tagToRemove: string) => {
        setTags(tags.filter((tag) => tag !== tagToRemove));
    };

    return (
        <div>
            <div className={`flex flex-wrap gap-2 rounded-md ${tags.length !== 0 && 'mb-3'}`}>
                {tags.map((tag, index) => (
                    <span key={index} className="transition-all border bg-secondary text-secondary-foreground hover:bg-secondary/80 inline-flex h-8 items-center text-sm pl-2 rounded-md">
                        {tag}
                        <Button
                            type="button" 
                            variant="ghost"
                            onClick={() => removeTag(tag)}
                            className={cn("py-1 px-3 h-full hover:bg-transparent")}
                        >
                            <X size={14} />
                        </Button>
                    </span>
                ))}
            </div>
            <Input
                ref={inputRef}
                type="text"
                placeholder={placeholder}
                value={inputValue}
                onChange={handleInputChange}
                onKeyDown={handleKeyDown}
                className={className}
            />
        </div>
    );
});

TagInput.displayName = 'TagInput';

export { TagInput };

Usage

import {
      Form,
      FormControl,
      FormDescription,
      FormField,
      FormItem,
      FormLabel,
      FormMessage,
    } from "@/components/ui/form"
    import React from 'react'
    import { TagInput } from '@/components/tag-input'

    const FormSchema = z.object({
      topics: z.array(z.string()),
    })

    const [tags, setTags] = React.useState<string[]>([]);

    const { setValue } = form;

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

    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
        <FormField
          control={form.control}
          name="topics"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Topics</FormLabel>
              <FormControl>
                <TagInput
                  {...field}
                  placeholder="Enter a topic"
                  tags={tags}
                  className='sm:min-w-[450px]'
                  setTags={(newTags) => {
                    setTags(newTags);
                    setValue("topics", newTags as [string, ...string[]]);
                  }} 
                />
              </FormControl>
              <FormDescription>
                These are the topics that you&apos;re interested in.
              </FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>



Related Posts

Recent Posts

ഇടുക്കിയിലെ മലയോര മേഖലകളിൽ രാത്രിയാത്ര നിരോധിച്ചു. രാത്രി ഏഴു മുതൽ രാവിലെ ആറു വരെയാണ് നിരോധനം

ഏന്തയാർ ഈസ്റ്റിൽ പ്രളയത്തിൽ തകർന്ന പാലത്തിന് പകരം പുതിയ പാലം നിർമ്മിക്കുവാൻ താത്ക്കാലിക പാലം പൊളിച്ച് നീക്കി

Explore the Investment Opportunities: A Comprehensive Guide to Different Types of Mutual Funds

Title: Understanding Mutual Funds: A Beginner's Guide to Investing

തീവ്രമഴ മുന്നറിയിപ്പിന്റെ പശ്ചാതലത്തിൽ സംസ്ഥാനം ജാഗ്രതയിൽ

250,000 അപേക്ഷകൾ വർദ്ധിച്ചതിനാൽ ട്രാൻസ്‌പോർട്ട് കമ്മീഷണർ പരിശോധന പുനരാരംഭിക്കും

ഏലക്കയിൽ കീടനാശിനി സാന്നിധ്യം; ആറര ലക്ഷത്തിലധികം ടിൻ അരവണ നശിപ്പിക്കാൻ ടെൻഡർ ക്ഷണിച്ച് ദേവസ്വം ബോർഡ്‌

ഭീമൻ പാറക്കഷണങ്ങൾ അടർന്ന് ദേശീയ പാതയിലേക്ക് വീഴുന്നത് പതിവാകുന്നു. കുട്ടിക്കാനത്തിനും മുണ്ടക്കയത്തിനുമിടയിൽ നിലനിൽക്കുന്നത് വൻ അപകട ഭീഷണി

ചക്രവാതച്ചുഴി:അതിശക്തമായ മഴ വരുന്നു

പ്ലസ് വൺ പ്രവേശനം. അക്ഷയയിൽ തിക്കി തിരക്കേണ്ട, നെറ്റിവിറ്റി/ജാതി തെളിയിക്കാൻ പത്താംതരം സർട്ടിഫിക്കറ്റ് മതി