Custom Comment System Development with Laravel and React

When your website grows, comments become a bottleneck. Built-in solutions like Disqus are a trade-off: you lose data control, page load speed suffers, and third-party ads annoy users. Server throughput decreases, infrastructure costs rise. We build custom comment systems on Laravel and React that so

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1281
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    977
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1026
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1103
  • image_website-_0.webp
    Website development for Red Pear
    550

When your website grows, comments become a bottleneck. Built-in solutions like Disqus are a trade-off: you lose data control, page load speed suffers, and third-party ads annoy users. Server throughput decreases, infrastructure costs rise. We build custom comment systems on Laravel and React that solve N+1 queries, spam, and complex moderation. In 3–7 days you get a flexible, fast, scalable system fully under your control.

What Problems Does a Custom Comment System Solve?

N+1 Queries for Nested Comments

A typical mistake: loading replies with separate queries for each comment. On a page with 100 comments, that's 101 queries. The solution: eager loading with recursive fetching — one query retrieves the entire tree, and Laravel builds the hierarchy in memory. As noted in the Laravel documentation, proper relationship handling reduces queries to a minimum.

Spam and Moderation

We automatically filter comments with suspicious links, set rate limits (10 comments per minute), and send suspicious ones for manual review. For guests, we use CAPTCHA. Details can be seen below.

More About ModerationWe combine pre-moderation for new users and automatic rule-based checks. Comments containing more than two external links are automatically marked as spam. For registered users with high reputation, comments are published immediately.

Duplicate Likes

To prevent multiple votes from the same user, we use Redis caching with key comment_like:{comment_id}:{user_id}. This is more reliable than a separate table and faster.

How We Do It

Tech Stack

  • Backend: Laravel, PHP 8.3, PostgreSQL 16, Redis 7
  • Frontend: React 18, TypeScript, Tailwind CSS
  • Infrastructure: Docker, Nginx, GitHub Actions

Key Solution: Anti-N+1 Queries

// In the controller $comments = Comment::where('entity_type', $entityType) ->where('entity_id', $entityId) ->whereNull('parent_id') ->with(['user', 'replies' => function ($query) { $query->where('status', 'approved')->with('user'); }]) ->latest() ->paginate(20); 

This pattern reduces the number of queries from N+1 to 3 (root comments, replies, users).

Database Schema

CREATE TABLE comments ( id SERIAL PRIMARY KEY, entity_type VARCHAR(50) NOT NULL, -- 'article', 'product', 'post' entity_id INTEGER NOT NULL, parent_id INTEGER REFERENCES comments(id) ON DELETE SET NULL, user_id INTEGER REFERENCES users(id), author_name VARCHAR(100), -- for guests author_email VARCHAR(255), body TEXT NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending|approved|rejected|spam likes_count INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX ON comments(entity_type, entity_id, status, created_at); CREATE INDEX ON comments(parent_id); CREATE INDEX ON comments(user_id); 

API and Frontend Design

Laravel API

class CommentController extends Controller { // Fetch comments for an entity public function index(Request $request, string $entityType, int $entityId): JsonResponse { $comments = Comment::where('entity_type', $entityType) ->where('entity_id', $entityId) ->where('status', 'approved') ->whereNull('parent_id') ->with(['user:id,name,avatar', 'replies' => fn($q) => $q->where('status', 'approved')->with('user:id,name,avatar')]) ->latest() ->paginate(20); return response()->json($comments); } // Add a comment public function store(StoreCommentRequest $request, string $entityType, int $entityId): JsonResponse { $this->throttle('comments', 10, 60); $requiresModeration = !auth()->check() || auth()->user()->comments()->where('status', 'spam')->exists() || $this->containsSuspiciousLinks($request->body); $comment = Comment::create([ 'entity_type' => $entityType, 'entity_id' => $entityId, 'parent_id' => $request->parent_id, 'user_id' => auth()->id(), 'author_name' => auth()->user()?->name ?? $request->author_name, 'author_email' => auth()->user()?->email ?? $request->author_email, 'body' => $this->sanitize($request->body), 'status' => $requiresModeration ? 'pending' : 'approved', ]); if ($comment->status === 'approved') { $this->notifyParentAuthor($comment); } else { Notification::send( User::moderators()->get(), new CommentPendingNotification($comment) ); } return response()->json(CommentResource::make($comment), 201); } private function sanitize(string $body): string { return strip_tags($body, '<b><i><em><strong><a><br><p>'); } private function containsSuspiciousLinks(string $body): bool { preg_match_all('/<a[^>]+href=["\']?([^"\'> ]+)/i', $body, $matches); foreach ($matches[1] ?? [] as $url) { if (!str_contains($url, config('app.url'))) { return true; } } return false; } } 

React Comment Tree

interface Comment { id: number; user: { name: string; avatar: string } | null; author_name: string; body: string; likes_count: number; created_at: string; replies?: Comment[]; } function CommentThread({ entityType, entityId }: { entityType: string; entityId: number }) { const { data, isLoading } = useQuery({ queryKey: ['comments', entityType, entityId], queryFn: () => api.get(`/comments/${entityType}/${entityId}`), }); return ( <section aria-label="Comments"> <h2>Comments ({data?.meta.total ?? 0})</h2> <CommentForm entityType={entityType} entityId={entityId} /> {isLoading ? <CommentSkeleton /> : ( <ul className="comment-list"> {data?.data.map(comment => ( <CommentItem key={comment.id} comment={comment} depth={0} /> ))} </ul> )} </section> ); } function CommentItem({ comment, depth }: { comment: Comment; depth: number }) { const [showReplyForm, setShowReplyForm] = useState(false); return ( <li className={`comment depth-${depth}`}> <img src={comment.user?.avatar || '/default-avatar.png'} alt={comment.user?.name || comment.author_name} width={40} height={40} /> <div className="comment__content"> <header> <strong>{comment.user?.name || comment.author_name}</strong> <time dateTime={comment.created_at}> {new Date(comment.created_at).toLocaleDateString('en-US')} </time> </header> <p>{comment.body}</p> <footer> <LikeButton commentId={comment.id} count={comment.likes_count} /> {depth < 3 && ( <button onClick={() => setShowReplyForm(!showReplyForm)}>Reply</button> )} </footer> {showReplyForm && ( <CommentForm parentId={comment.id} onSubmit={() => setShowReplyForm(false)} /> )} {comment.replies?.map(reply => ( <ul key={reply.id}><CommentItem comment={reply} depth={depth + 1} /></ul> ))} </div> </li> ); } 

Why Custom Over Off-the-Shelf Widgets?

Criterion Custom Solution Disqus / Commento
Data control Full None (third-party)
Performance Optimized for your stack Extra HTTP request
Customization Unlimited Limited
Cost Development, then free Freemium / ads
GDPR / Privacy Full compliance Risks
Implementation variant Nesting Moderation Likes Timeline
Basic Flat Automatic No 3-4 days
Standard Up to 3 levels Automatic + manual Yes 5-7 days
Premium Up to 5 levels Custom rules + AI filter Yes + notifications Up to 10 days

Process

  1. Analysis — discuss requirements: nesting, moderation, notifications, auth integration.
  2. Design — DB schema, API endpoints, React components.
  3. Implementation — backend (Laravel), frontend (React), tests.
  4. Testing — load testing (1000 comments), spam filter verification.
  5. Deployment — Docker image, CI/CD, cache configuration.

Timeline and Cost

  • Basic system (flat, moderation): 3–4 days
  • With nesting, likes, notifications: 5–7 days
  • With social integration and custom anti-spam: up to 10 days

Cost is calculated individually — we assess your project within an hour. A custom comment system pays off in 2–4 months compared to paid subscriptions. Get a consultation on integrating a comment system into your project. For an accurate estimate, contact our engineer.

A custom comment system is an investment in user experience quality and independence from third-party services. We design for your scale: from a small blog with 50 comments per day to high-load platforms with 10,000 messages per day. The architecture is the same; only Redis caching parameters and horizontal scaling strategy change.

What's Included

  • Backend source code (Laravel) and frontend (React) with unit tests
  • API documentation and database structure
  • Docker environment setup and CI/CD pipeline
  • Integration with your authentication system
  • Access to a private repository
  • Team training (2–3 hours)
  • One month of technical support after launch

7+ years of web development experience, 50+ projects with custom comment systems. We guarantee transparency and quality.