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.