Textarea Mentions with Alpine.js and Tribute

February 6th, 2025 • 3 minutes read time

Adding mention triggers to textareas is easy with Tribute. In this article, we'll set up the ability to type a trigger (like @) and display/search a list of users for insertion.

Of course, you can transfer this to anything — it doesn't have to be usernames or users!

We're using Laravel for this article, but the Alpine-specific functionality will transfer to any project.

First up, let's install Tribute:

npm i tributejs

Because we will deal with an Alpine component, we'll import and set Tribute to the JavaScript window object. It's not the ideal solution, but it'll be fine for most cases.

Open up your main app.js file and add the following:

import Tribute from 'tributejs'
import 'tributejs/dist/tribute.css'

window.Tribute = Tribute

As you can see, we also pull in the tribute.css stylesheet.

Here's the textarea I'm starting with. Nothing fancy, and styled with Tailwind.

<div>
    <textarea class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm w-full" rows="4"></textarea>
</div>

Let's make this an Alpine.js component and set up Tribute to bind to the textarea. We'll use some hardcoded dummy data for now.

<div
    x-data
    x-init='
        let tribute = new Tribute({
            trigger: "@",
            values: [{ key: "Alex", value: "alex" }, { key: "Tabby", value: "tabby" }]
        })

        tribute.attach($refs.textarea)
    '
>
    <textarea class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm w-full" rows="4" x-ref="textarea"></textarea>
</div>

Make sure you add x-ref to the textarea.

The result:

blog/9HmlXNTmGWZXSyN3Q9F5jVSYYz8o5V1LeuvrEGUJ.webp

So, with that tiny snippet of code added, you now have mentions with Alpine and Tribute! There are many ways to customise Tribute, which you'll find in the documentation.

For example, you can customise how the results are rendered like this:

let tribute = new Tribute({
    trigger: "@",
    values: [{ key: "Alex", value: "alex" }, { key: "Tabby", value: "tabby" }],
    menuItemTemplate: (item) => {
        return "@" + item.original.value + " (" + item.original.key + ")"
    }
})

And that makes things a little clearer:

blog/s22YcLlwdMivKYRKlMGkahyTVmbYngZSPzA1z1bn.webp

Right now, we're hardcoding the data. There are a few options here, but we will stick with the simplest for now — dumping all of our potential values under the values array.

This solution works well for hundreds or potentially thousands of users but will eventually start to slow down. If so, you'll eventually want to perform a remote search with Tribute.

With Laravel, we can grab a list of users from our app and add them to our mention functionality.

To start, we'll create a class which returns and structures a collection of users in the way we need:

namespace App\Mentions;

use App\Models\User;

class Mentionables
{
    public static function get()
    {
        return User::get()
            ->map(fn (User $user) => [
                'key' => $user->name,
                'value' => $user->username
            ]);
    }
}

Once we have this, we can JSON encode it and add it directly to the values within Tribute:

 let tribute = new Tribute({
    trigger: "@",
    values: @json(App\Mentions\Mentionables::get()),
    menuItemTemplate: (item) => {
        return "@" + item.original.value + " (" + item.original.key + ")"
    }
})

And with that, all users in your application will now be available in the dropdown list Tribute gives you when you hit the trigger key.

If you enjoyed this article, you'll probably find the Laravel Mentions course helpful. We cover everything in the article and create the core functionality around mentioning and notifying users in Laravel.

Happy mentioning!

If you found this article helpful, you'll love our practical screencasts.
Author
Alex Garrett-Smith
Share :

Comments

No comments, yet. Be the first!