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

react-simple-mentions

v1.7.0

Published

Simple mentions input using textarea. Lightweight, customizable and built with Typescript.

Readme

react-simple-mentions

A lightweight and customizable React component for handling mentions in text inputs. Perfect for building chat applications, comment sections, or any feature requiring user mentions.

View an example: https://react-simple-mentions-vite.vercel.app/

https://github.com/user-attachments/assets/1c4fcf2f-01c9-4276-ad45-02f977204c17


Features

  • Parse and display mentions in a user-friendly format (e.g., @username).
  • Insert mentions programmatically.
  • Trigger mention suggestions with @ and capture queries.
  • Fully customizable and easy to integrate.
  • Supports TypeScript for type safety.

Installation

Install the package via NPM:

npm install react-simple-mentions

Or with Yarn:

yarn add react-simple-mentions

Usage

Here’s a basic example of how to use the MentionsInput component:

Basic Example

Browse complete examples in the examples folder.

import React, { useRef, useState } from "react";
import MentionsInput, {
  Mention,
  MentionsInputRef,
} from "react-simple-mentions";

const App = () => {
  const [value, setValue] = useState("");
  const mentionsInputRef = useRef<MentionsInputRef>(null);

  const handleMentionTrigger = (data: { query: string; position: number }) => {
    console.log("Mention triggered:", data);
  };

  const handleInsertMention = () => {
    const mention: Mention = { name: "John Doe", id: "123" };
    mentionsInputRef.current?.insertMention(mention);
  };

  return (
    <div>
      <MentionsInput
        ref={mentionsInputRef}
        value={value}
        onChange={setValue}
        onMentionTrigger={handleMentionTrigger}
        placeholder="Type @ to mention someone..."
        rows={5}
        style={{ width: "100%", padding: "10px" }}
      />
      <button onClick={handleInsertMention}>Insert Mention</button>
    </div>
  );
};

export default App;

Props

MentionsInput Props

| Prop | Type | Description | | ------------------ | ------------------------------------ | ------------------------------------------------------- | | value | string | The current value of the input. | | onChange | (value: string) => void | Callback triggered when the input value changes. | | onMentionTrigger | (data: MentionTriggerData) => void | Callback triggered when a mention query is detected. | | onMentionCancel | () => void | Callback triggered when the current mention is deleted. |

  • All standard HTML textarea props.

API

MentionsInputRef

The MentionsInput component exposes the following methods via its ref:

| Method | Description | | --------------- | ---------------------------------------------------------------------------- | | insertMention | Inserts a mention programmatically. Takes a Mention object as an argument. | | cancelMention | Cancels the current mention query. | | focus | Focuses the input programmatically. | | element | The underlying textarea element ref. |

MentionTriggerData

The onMentionTrigger callback provides the following data:

| Field | Type | Description | | -------------- | -------- | -------------------------------------------------------- | | query | string | The current query string after the @ symbol. | | position | number | The cursor position in the input. | | mentionStart | number | The starting position of the mention query in the input. |


Customizing Mentions

Mentions are stored in the format @[name](id) internally but displayed as @name in the input. This allows you to easily parse and manage mentions in your application.

Parsing Mentions

You can use the parseMentions function to extract mentions from the stored format:

const mentions = parseMentions(value);
console.log(mentions);
/*
[
  { name: "John Doe", id: "123", start: 0, end: 15 },
  { name: "Jane Smith", id: "456", start: 16, end: 31 }
]
*/

Styling

You can style the MentionsInput component using the className or style props. For example:

<MentionsInput
  className="custom-mentions-input"
  style={{ border: "1px solid #ccc", borderRadius: "4px" }}
/>

Mentions Picker Example

Below is a simple example of rendering your own mentions picker component:

import React, { useState, useEffect } from "react";
import type { Mention } from "react-simple-mentions";

interface MentionsPickerProps {
  isOpen: boolean;
  query: string;
  onSelect: (mention: Mention) => void;
  onClose: () => void;
  position?: { x: number; y: number };
}

// Mock data for testing
const MOCK_USERS: Mention[] = [
  { id: "1", name: "John Doe" },
  { id: "2", name: "Jane Smith" },
  { id: "3", name: "Bob Johnson" },
  { id: "4", name: "Alice Brown" },
  { id: "5", name: "Charlie Wilson" },
];

const MentionsPicker: React.FC<MentionsPickerProps> = ({
  isOpen,
  query,
  onSelect,
  onClose,
  position,
}) => {
  const [selectedIndex, setSelectedIndex] = useState(0);

  // Filter users based on query
  const filteredUsers = MOCK_USERS.filter((user) =>
    user.name.toLowerCase().includes(query.toLowerCase())
  );

  // Reset selection when query changes
  useEffect(() => {
    setSelectedIndex(0);
  }, [query]);

  // Handle keyboard navigation
  useEffect(() => {
    if (!isOpen) return;

    const handleKeyDown = (e: KeyboardEvent) => {
      switch (e.key) {
        case "ArrowDown":
          e.preventDefault();
          setSelectedIndex((prev) =>
            prev < filteredUsers.length - 1 ? prev + 1 : 0
          );
          break;
        case "ArrowUp":
          e.preventDefault();
          setSelectedIndex((prev) =>
            prev > 0 ? prev - 1 : filteredUsers.length - 1
          );
          break;
        case "Enter":
          e.preventDefault();
          if (filteredUsers[selectedIndex]) {
            onSelect(filteredUsers[selectedIndex]);
          }
          break;
        case "Escape":
          e.preventDefault();
          onClose();
          break;
      }
    };

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [isOpen, selectedIndex, filteredUsers, onSelect, onClose]);

  if (!isOpen || filteredUsers.length === 0) return null;

  const style: React.CSSProperties = {
    position: "absolute",
    top: position ? position.y + 20 : 0,
    left: position ? position.x : 0,
    backgroundColor: "white",
    border: "1px solid #ccc",
    borderRadius: "4px",
    boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
    zIndex: 1000,
    maxHeight: "200px",
    overflowY: "auto",
    minWidth: "200px",
  };

  return (
    <div style={style}>
      {filteredUsers.map((user, index) => (
        <div
          key={user.id}
          onClick={() => onSelect(user)}
          style={{
            padding: "8px 12px",
            cursor: "pointer",
            backgroundColor: index === selectedIndex ? "#f0f0f0" : "white",
            borderBottom:
              index < filteredUsers.length - 1 ? "1px solid #eee" : "none",
          }}
          onMouseEnter={() => setSelectedIndex(index)}
        >
          <div style={{ fontWeight: "500" }}>{user.name}</div>
          <div style={{ fontSize: "12px", color: "#666" }}>ID: {user.id}</div>
        </div>
      ))}
    </div>
  );
};

export default MentionsPicker;

License

This project is licensed under the MIT License. See the LICENSE file for details.


Contributing

Contributions are welcome! Feel free to open issues or submit pull requests to improve the package.


Author

Created by leojuriolli7.