DEV Community

Yoni Ryabinski
Yoni Ryabinski

Posted on • Originally published at echothread.io

How to Add Comments to a Next.js Site (App Router and Pages Router)

Next.js gives you so many ways to render a page that the answer to "how do I add comments" depends on which version you're on and which architecture you've chosen. Here's a single approach that works for both the App Router and the Pages Router: a small client component that wraps the EchoThread widget (privacy-first, no ads, under 15 KB gzipped, free during beta).

1. Get an API key

Sign up at echothread.io, add your domain, copy the key.

2. Add to .env.local

NEXT_PUBLIC_ECHOTHREAD_API_KEY=YOUR_API_KEY
Enter fullscreen mode Exit fullscreen mode

The NEXT_PUBLIC_ prefix is mandatory for client-side access.

3. Create components/EchoThread.tsx

"use client";

import Script from "next/script";

export default function EchoThread({ theme = "auto" }: { theme?: string }) {
  const apiKey = process.env.NEXT_PUBLIC_ECHOTHREAD_API_KEY;
  if (!apiKey) return null;

  return (
    <section
      className="echothread-wrapper"
      style={{ marginTop: "4rem", paddingTop: "2rem", minHeight: 400 }}
    >
      <div id="echothread" data-api-key={apiKey} data-theme={theme}></div>
      <Script src="https://cdn.echothread.io/widget.js" strategy="lazyOnload" />
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

The lazyOnload strategy ensures the widget never blocks LCP.

4. Use it in your post page

App Router (app/blog/[slug]/page.tsx):

import EchoThread from "@/components/EchoThread";

export default async function Post({ params }) {
  const post = await getPost(params.slug);
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
      <EchoThread />
    </article>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pages Router is essentially identical — same component, just imported into your pages/blog/[slug].tsx.

5. Handle soft navigation

Next.js does client-side navigation between routes. To make sure the widget refreshes when readers click between posts, force a remount with a key prop:

"use client";
import { usePathname } from "next/navigation";
import EchoThread from "@/components/EchoThread";

export default function Wrapper() {
  const pathname = usePathname();
  return <EchoThread key={pathname} />;
}
Enter fullscreen mode Exit fullscreen mode

For Pages Router, use useRouter().asPath instead.

Done

The widget identifies threads by page URL automatically. Server components stay server components — only the comment island hydrates.

Full guide with troubleshooting and CSP notes: echothread.io/docs/guides/nextjs

Top comments (0)