How Upshotly implemented user mentions in comments using the react-mentions library

Ganesh Ravi Shankar
Tech @ Upshotly
Published in
7 min readJul 4, 2019

Implementing Comment with Mentions

So we at Upshotly tried to implement a “Comments” feature with the capability to “Mention” other people within our product. To give you an idea of what they mean, it’s very much like the Comments and Mentions you would have used to tag your friends on Facebook or Instagram memes. From here on, we’ll be using these two words without quotes in this context. In case you are implementing comments for any resource/feature in your organization, you would also probably want to allow people to tag people or places in the comments. If you are using react as your front-end, you might consider using the two possible famous libraries for including mentions in your comments.

  1. react-mentions
  2. DraftJs mentions

You can achieve your goal of implementing mentions using both libraries. But IMO using react-mentions was easier than using DraftJs mentions plugins.

Comparing react-mentions with DraftJS mentions plugin

Let’s try and compare both react-mentions and DraftJs mentions plugin in terms of

  1. Implementation
  2. UI

Comparing in terms of implementation:

Implementing mentions for comments was so much more comfortable in react-mentions when compared to drafts mentions plugins and here’s why

  1. It is configurable

React uses markup and hence is more comfortable to save, retrieve, or show the comment.
Reactions mentions use something called markup that wraps your mentioned text with a markup which you can configure on your own.

For Example: When you mention someone, it wraps the mentioned user as @[username][userId] along with the other “text/comment” (Hi @[John][123] how are you doing?). Whereas DraftJs plugin gives the text and mentions separately, like (Hi John, how are you doing and a separate array of mentions as [John]), Which makes it challenging to save and retrieve from the database or show it in the whole comment.

2) It is format friendly

React mentions support something called display transform (https://github.com/signavio/react-mentions) in the Mention component which allows you to transform your mentions username into whatever format you need.

3) It is easy to implement

React mentions allows only userName and Id which is pretty much easy to implement (fewer queries to backend of-course).

Comparison in terms of UI

In the case of UI, the DraftJs plugin looks much better than react-mention and here’s why:

When coming to the mentions UI, DraftJs plugin is far better than react-mentions. DraftJs supports three items in the mentions list:

  1. User Image
  2. User Link
  3. User Name

Whereas react mentions supports only two:

  1. User Name
  2. User Id

Implementing mentions with react-mentions

Let’s see how we can implement a mention in your comment box in both front and backend.

The first step is to install react-mentions in your project:

npm install react-mentions — save

or using Yarn

yarn add react-mentions

The second step is to import it into your component:

import { MentionsInput, Mention } from ‘react-mentions’

Once you complete these two steps, you’re all set to get started.

Let’s include this code below in your component where you want your comment box to be:

<MentionsInput value={this.state.comment} onChange={event => this.setState({comment: event.target.value})}>
<Mention
trigger=”@”
data={this.props.users}
/>
</MentionsInput>

As a result, you would get something like the below image:

MentionInput TextArea

MentionInput is nothing but a textarea. After you include this in the component while rendering it renders two div’s one is the highlighter that’s hidden in the background of your textarea, and the other is the text area comment component. Highlighter is responsible for highlighting the mentions in your comment with a color you specify. You have to pass props users to this component so that the data loads the users from the props.

The format of the user's props should be:

users =[
{
id: user_id
display: user_name
}
]

It’s done! You are all good to go. Just reload your page and you can see the comment box in your component.

Just type in @ in your comment box and you will see all the users listed in the props you have given.

Saving comments along with mentions to database

The central part is how you save it to the database. So that when you show the comment, all the mentioned users are clickable, and on clicking them, it takes you to their specific user profile page.
So after you have completed entering your comment along with mentions, you can write the logic to handle the saving to database part on onBlur event of your MentionInput, or you can have a submit button onClick event.

As I mentioned earlier you can have a markUp to the MentionInput as shown below:

<MentionsInput value={this.state.comment} onChange={event =>   this.setState({comment: event.target.value})}>
<Mention
trigger=”@”
data={this.props.users}
markup=‘@@@____id__^^____display__@@@^^^’
/>
</MentionsInput>
<Button className=”btn btn-us” onClick={() => this.saveComment()}>
Submit
</Button>

You can fill the same markup with any format you like. Here, I’m using it to show how it can be of any length. Now that we’ve set the markup, let’s see how to save it to the database on the submit button onClick event.

saveComment = () => {
let newComment = this.state.comment;
newComment = newComment.split(‘@@@__’).join(“<a href=\”/user/”)
newComment = newComment.split(‘^^^__’).join(“\”>”)
newComment = newComment.split(‘@@@^^^’).join(“</a>”);
if (newComment != ‘’) {
let comment = newComment.trim();
//Call to your DataBase like backendModule.saveComment(comment, along_with_other_params);
this.setState({
comment: ‘’,
})
}
}

So this will save your comment along with the mentions as shown below in your database:

How are you <a href=”/user/1”>John Doe</a> and <a href=”/user/2”>Amanda</a>.

How to display a saved comment from the database in UI

Once you have saved the comment, you can query your backend from react to get the comment. On getting the comment it gets populated in your component props. The above instruction is given with the assumption that you know how to connect to the store and map state to props.

So inside the component, once you have queried your comment will be in this.props.comment.

Let's say your DB scheme is like

Comments {
id: Int
comment_text: varchar
.
.
.
other_fields
}

Note: You cannot render a string from the backend as HTML in your div/span/p or any tags.
So in react you can use dangerouslySetInnerHTML. So this is how you can render the saved comment:

<p dangerouslySetInnerHTML={{ __html: comment.comment.comment_text.replace(/\n\r?/g, ‘<br />’) }}/>dangerouslySetInnerHTML - There are so many alternative to avoid using this :) See this.

Why I have used comment_text.replace(/\n\r?/g, ‘<br/>)

Let us say you have a comment with more than one line. So each comment with has ‘\n’ in it to represent a new line. HTML tags by default won’t consider ‘\n’ as a new line so I have used comment_text.replace(/\n\r?/g, ‘<br/>) to replace all the ‘\n’ with <br/>, so that the comment gets displayed the same way you typed it.

Changing the UGLY UI to look sharp and fresh:

One thing about react-mentions was that the default UI sucks.
We tried writing CSS for it and applied to it:

.comments-textarea{
background-color: #fff;
border-radius: 3px;
transition: 0.3s ease-in-out;
padding: 9px;
font-size: 14px;
line-height: 1.42857143;
color: #333;
border: 1px solid #dedede;
&:focus,&:active{
outline: none;
border: 1px solid #3fb9de;
}
}
<MentionsInput className=“comments-textarea” value={this.state.comment} onChange={event => this.setState({comment: event.target.value})}>
<Mention
trigger=”@”
data={this.props.users}
markup=‘@@@____id__^^____display__@@@^^^’
/>
</MentionsInput>

But to our surprise, the class was not applied though we gave the correct class name

Then after some hacking and searching, we found that react-mentions libraries append some form of string to the class name we specify to each element in it;

  1. For MentionInput input it appends __input at the end of our className (comments-textarea)
  2. For Suggestions list, it appends __suggestions__list at the end of our className (comments-textarea)
  3. For Suggestion Items, it appends __suggestions_item at the end of our className (comments-textarea)
  4. For Suggestion item when focused it appends __suggestions__item — focused at the end of our className (comments-textarea)
  5. For the Input Control, it appends __control at the end of our className (comments-textarea)
  6. For the Highlighter, it appends __highlighter at the end of our className (comments-textarea).

Find our CSS changes below for the entire mentions input

.comments-textarea__control{
margin-top: 10px;
margin-bottom: 20px;
}
.comments-textarea__input{
background-color: #fff;
border-radius: 3px;
transition: 0.3s ease-in-out;
padding: 9px;
font-size: 14px;
line-height: 1.42857143;
color: #333;
border: 1px solid #dedede;
&:focus,&:active{
outline: none;
border: 1px solid #3fb9de;
}
&::placeholder {
color: #a4b0be;
font-size: 14px;
}
}
.comments-textarea__highlighter{
padding: 10px;
}
.comments-textarea__suggestions__list {
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.15);
font-size: 14px;
max-height: 200px;
overflow: auto;
}
.comments-textarea__suggestions__item {
padding: 5px 15px;
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
}
.comments-textarea__suggestions__item — focused {
background-color: #daf4fa;
}

How to change the color of your mentions highlighter

As I mentioned before MentionInput gets rendered as two div’s one is the highlighter which highlights the mentions in your comment textarea with the color. In order to change the color of that highlight, you can give something as shown below.

<MentionsInput className=’comments-textarea’ placeholder=”Add Comment” value={this.state.comment} onChange={(event) => this.setState({ comment: event.target.value })}>
<Mention
trigger=”@”
markup=’@@@____id__^^^____display__@@@^^^’
displayTransform={this.handleDisplayTextForMention}
data={this.props.users}
style={{
backgroundColor: ‘#daf4fa’
}}

/>
</MentionsInput>

The final output will be as shown:

So that’s how we implemented comments with mentions in Upshotly. Upshotly aims to provide continuous performance management tools for modern leaders to put people at the heart of their business success.

Feel free to reach me in case if you have any comments regarding this article.

--

--