npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mshafiqyajid/react-tag-input

v1.3.0

Published

Headless tag/chip input hook and styled component for React. Autocomplete, keyboard navigation, validation, fully typed.

Readme

@mshafiqyajid/react-tag-input

Tag/chip input with autocomplete for React. Headless hook + styled component. Keyboard navigation, validation, duplicate prevention, fully typed.

Install

npm install @mshafiqyajid/react-tag-input

Headless usage

import { useTagInput } from "@mshafiqyajid/react-tag-input";

function MyTagInput() {
  const { tags, inputProps, removeTag, filteredSuggestions, activeIndex } = useTagInput({
    suggestions: ["React", "Vue", "Svelte", "Angular"],
    maxTags: 5,
  });

  return (
    <div>
      {tags.map((tag, i) => (
        <span key={i}>
          {tag} <button onClick={() => removeTag(i)}>×</button>
        </span>
      ))}
      <input {...inputProps} placeholder="Add a tag..." />
      {filteredSuggestions.length > 0 && (
        <ul>
          {filteredSuggestions.map((s, i) => (
            <li key={s} aria-selected={i === activeIndex}>{s}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

Styled usage

import { TagInputStyled } from "@mshafiqyajid/react-tag-input/styled";
import "@mshafiqyajid/react-tag-input/styles.css";

function App() {
  const [tags, setTags] = useState<string[]>([]);

  return (
    <TagInputStyled
      value={tags}
      onChange={setTags}
      suggestions={["React", "Vue", "Svelte", "Angular"]}
      label="Technologies"
      placeholder="Add a tag..."
      tone="primary"
      size="md"
    />
  );
}

Paste handling

<TagInputStyled
  value={tags}
  onChange={setTags}
  // Default delimiters split pasted text by comma, semicolon, newline, tab.
  // Pass null to disable paste-splitting.
  pasteDelimiters={[",", ";", /\s*\n\s*/, /\s*\t\s*/]}
  // Normalize each tag before insertion.
  transform={(t) => t.toLowerCase().trim()}
/>

Props (additions)

| Prop | Type | Default | Description | |------|------|---------|-------------| | pasteDelimiters | (string \| RegExp)[] \| null | [",", ";", /\s*\n\s*/, /\s*\t\s*/] | Split pasted text into multiple tags. null disables paste-splitting. | | transform | (tag) => string | — | Normalize each tag before insertion (e.g. lowercase, trim). | | backspaceEditsLastTag | boolean | false | When the input is empty, Backspace pops the last tag back into the input for editing instead of deleting it. | | tagVariant | "solid" \| "subtle" \| "outline" | "solid" | Visual style for tag chips. | | reorderable | boolean | false | Enable HTML5 drag-to-reorder. Tags get cursor: grab. Drop fires onChange + onReorder. | | loadSuggestions | (query: string) => Promise<string[]> | — | Async suggestions loader. Replaces static suggestions. Debounced, cancellable. Shows a spinner while loading. | | debounceMs | number | 300 | Debounce delay for loadSuggestions / loadOptions. | | loadingText | ReactNode | "Loading…" | Text shown in the dropdown while loadSuggestions is in flight. |

Docs

https://docs.shafiqyajid.com/react/tag-input/

License

MIT

What's new in 1.3.0

  • reorderable?: boolean — drag-to-reorder tags with HTML5 drag events. Drop indicator (rti-tag--drop-target) and dragging cursor (rti-tag--dragging). onReorder callback fires on drop. Spring animation on release (cubic-bezier(0.34, 1.56, 0.64, 1)).
  • Edit-in-place — double-click any tag to edit inline. Enter commits, Escape cancels, blur commits. CSS class rti-tag--editing. Disabled/readOnly blocks editing.
  • loadSuggestions?: (query: string) => Promise<string[]> — async drop-in replacement for suggestions. Debounced 300 ms, cancellable via AbortController. Dropdown shows an animated spinner while loading. Props debounceMs and loadingText apply. The static suggestions prop still works when loadSuggestions is absent.
  • Motion — tags scale in (0 → 1.08 → 1) on add and scale + fade out on remove. All animations respect prefers-reduced-motion.

What's new in 1.2.0

  • Async loadOptions: (query: string) => Promise<string[]> — debounced (default 300 ms), cancellable via AbortController, with isLoading + loadError exposed.
  • colorize: (tag: string) => string — derive a CSS color per tag (category-based, hash-based) and apply as the chip background. data-colorized lands for further styling hooks.
  • tagActions: (tag, index) => ReactNode — render extra buttons inside each chip (rename, link, copy).
  • onReorder: (tags: string[]) => void — fires after a drag-reorder commits (alongside the existing onChange).
  • Spreadsheet TSV pastespreadsheetPaste: true (default) treats \n and \t as cell breaks alongside the existing comma/semicolon split.