A modern collaboration or social interaction module is incomplete without a rich comment system. Applications such as Slack, GitHub, Facebook, Jira, and internal enterprise tools allow users to mention other users using the @username format and categorize or highlight topics using #hashtags.
This article explains how to build a complete mentions and hashtags–enabled comment system using Angular (frontend) and ASP.NET Core (backend) with a scalable database design in SQL Server.
The objective is to design a reusable, production-ready module suitable for enterprise web applications.
1. Key Functional Requirements
Users can type comments.
Auto-suggestions appear when typing
@for users.Auto-suggestions appear when typing
#for tags.Comments should highlight mentions and hashtags.
Backend must store extracted mentions and tags.
Display comments with clickable mentions and hashtags.
Should support comment editing and deletion.
2. High-Level Workflow
User types comment →
Detect @mention or #tag →
Show suggestion list →
User selects item →
Submit comment →
Backend processes comment →
Save comment, mentions, tags →
Return formatted comment →
Display in UI
Flowchart (Comment Entry + Processing)
+----------------------+
| User types comment |
+---------+------------+
|
v
+--------------------------+
| Detect @ or # character |
+------------+-------------+
|
+----------+-----------+
| |
v v
+-----------------+ +-----------------+
| Load user list | | Load tag list |
+--------+--------+ +--------+--------+
| |
v v
+-----------------+ +-----------------+
| User selects | | User selects |
| mention | | tag |
+--------+--------+ +--------+--------+
| |
+-----------+-----------+
|
v
+------------------------+
| Submit final comment |
+-----------+------------+
|
v
+--------------------------+
| Backend extracts tokens |
+-----------+--------------+
|
v
+--------------------------+
| Save in database |
+-----------+--------------+
|
v
+--------------------------+
| Return formatted comment |
+--------------------------+
3. ER Diagram (SQL Server)
+------------------------+
| Users |
+------------------------+
| UserId (PK) |
| DisplayName |
| Email |
+------------------------+
+------------------------+
| Comments |
+------------------------+
| CommentId (PK) |
| UserId (FK) |
| Content |
| CreatedOn |
+------------------------+
+---------------------------+
| CommentMentions |
+---------------------------+
| Id (PK) |
| CommentId (FK) |
| MentionedUserId (FK) |
+---------------------------+
+---------------------------+
| Tags |
+---------------------------+
| TagId (PK) |
| TagName |
+---------------------------+
+---------------------------+
| CommentTags |
+---------------------------+
| Id (PK) |
| CommentId (FK) |
| TagId (FK) |
+---------------------------+
4. Architecture Diagram (Visio Style)
+------------------------------+
| Angular App |
+------------------------------+
| Type Comment
v
+--------------------------+
| Comment Component |
+------------+-------------+
|
| API Calls
v
+-------------------------------+
| ASP.NET Core API (Web API) |
+-------------------------------+
| Validate | Parse | Save Data |
|
v
+-------------------------------+
| SQL Server (Comments, Tags) |
+-------------------------------+
5. Sequence Diagram
User → Angular UI: Type comment
Angular UI → SuggestionService: Detect @ or #
SuggestionService → API: Fetch matching users or tags
API → Angular: Return suggestions
User → Angular: Select mention/tag
User → Angular: Submit final comment
Angular → API: Send comment text
API → Parser: Extract @mentions and #tags
Parser → DB: Save comment, mentions, tags
DB → API: Confirm save
API → Angular: Return formatted comment
Angular → User: Show updated comment list
6. Frontend Implementation (Angular)
Detecting Mentions and Hashtags in Textbox
<textarea
[(ngModel)]="commentText"
(keyup)="onKeyUp($event)"
placeholder="Write a comment...">
</textarea>
<div *ngIf="showSuggestions">
<ul class="suggestions">
<li *ngFor="let item of suggestions"
(click)="selectSuggestion(item)">
{{ item.display }}
</li>
</ul>
</div>
TypeScript Logic
onKeyUp(event: KeyboardEvent) {
const text = this.commentText;
const cursorPos = (event.target as HTMLTextAreaElement).selectionStart;
const charBeforeCursor = text[cursorPos - 1];
if (charBeforeCursor === '@') {
this.loadUserSuggestions();
}
if (charBeforeCursor === '#') {
this.loadTagSuggestions();
}
}
loadUserSuggestions() {
this.api.getUsers().subscribe(res => {
this.showSuggestions = true;
this.suggestions = res.map(u => ({ display: u.displayName, type: 'user', id: u.userId }));
});
}
loadTagSuggestions() {
this.api.getTags().subscribe(res => {
this.showSuggestions = true;
this.suggestions = res.map(t => ({ display: t.tagName, type: 'tag', id: t.tagId }));
});
}
selectSuggestion(item: any) {
const prefix = item.type === 'user' ? '@' : '#';
this.commentText += prefix + item.display + ' ';
this.showSuggestions = false;
}
7. Submitting the Comment
submitComment() {
this.api.postComment({ content: this.commentText }).subscribe(() => {
this.commentText = '';
this.loadComments();
});
}
8. Backend Implementation (ASP.NET Core)
Extracting Mentions and Tags
var mentions = Regex.Matches(model.Content, @"@(\w+)");
var tags = Regex.Matches(model.Content, @"#(\w+)");
API Controller
[HttpPost("comments")]
public async Task<IActionResult> PostComment(CommentDto model)
{
var comment = new Comment
{
UserId = model.UserId,
Content = model.Content,
CreatedOn = DateTime.UtcNow
};
_db.Comments.Add(comment);
await _db.SaveChangesAsync();
// Extract mentions
var mentions = Regex.Matches(model.Content, @"@(\w+)")
.Select(m => m.Groups[1].Value)
.ToList();
foreach (var m in mentions)
{
var user = _db.Users.FirstOrDefault(u => u.DisplayName == m);
if (user != null)
{
_db.CommentMentions.Add(new CommentMention
{
CommentId = comment.CommentId,
MentionedUserId = user.UserId
});
}
}
// Extract hashtags
var tags = Regex.Matches(model.Content, @"#(\w+)")
.Select(m => m.Groups[1].Value)
.ToList();
foreach (var t in tags)
{
var tag = _db.Tags.FirstOrDefault(x => x.TagName == t)
?? new Tag { TagName = t };
_db.CommentTags.Add(new CommentTag
{
CommentId = comment.CommentId,
TagId = tag.TagId
});
}
await _db.SaveChangesAsync();
return Ok();
}
9. Displaying Mentions and Hashtags with Highlighting
Angular Pipe
transform(value: string): string {
value = value.replace(/@(\w+)/g, `<span class="mention">@$1</span>`);
value = value.replace(/#(\w+)/g, `<span class="hashtag">#$1</span>`);
return value;
}
10. UI Styling
.mention { color: #0275d8; font-weight: bold; }
.hashtag { color: #5cb85c; font-weight: bold; }
.suggestions { list-style: none; margin: 0; padding: 0; background: #f5f5f5; }
.suggestions li { padding: 6px; cursor: pointer; }
11. Best Practices
Use caching for user and tag suggestions to improve performance
Avoid over-querying the database
Rate-limit comment submission
Support soft delete for comments
Implement pagination for comment lists
Add real-time support using SignalR if needed
Conclusion
A comment system with mentions and hashtags adds professional collaboration capabilities to any web application. By using Angular for real-time processing and ASP.NET Core for strong backend parsing, you can build a scalable and reusable module. The design shown here works for enterprise portals, task management systems, social feeds, project tracking tools, LMS platforms, and more.

Join the conversation! Your thoughts help the community grow.