Setting Up Disqus in a Next.js Blog
Disqus is the most popular commenting system available in the market right now. While its free nature comes with downsides (hint - you are the data!), its ease of setup and not having to maintain a self-hosted commenting solution makes it a compelling choice for small blogs like this one.
I have been using Disqus for the last 10 years across various static site generators, so it contains all of my previous comments. While migrating Disqus comments to a different URL scheme is a topic for another day, this article focuses on setting up Disqus in a Next.js blog.
We'll cover how to:
- Create a React component wrapper for Disqus
- Implement lazy loading when the component is in view
- Make Disqus respect theme toggling
- Resolve OKLCH color incompatibility issues
Without further ado, let's begin.
- You should have a Next.js blog.
next-themes
for handling the dark mode toggling.disqus-react
for providing Disqus script.react-intersection-observer
to implement lazy-loading.
Before implementing the component, you'll need to:
- Create a Disqus account at disqus.com
- Register your site and get your shortname
- Configure your site settings in the Disqus admin panel
npm install disqus-react next-themes react-intersection-observer
# OR
bun add disqus-react next-themes react-intersection-observer
Adapt the root layout to match the following snippet -
import { ThemeProvider } from "next-themes"
import "./globals.css"
interface RootLayoutProps {
children: React.ReactNode
}
export default function RootLayout({ children }: RootLayoutProps) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
)
}
Now add the following to the globals.css
file:
@import "tailwindcss";
@config '../tailwind.config.js';
@layer base {
:root {
--background: theme("colors.white");
--foreground: theme("colors.slate.950");
}
.dark {
--background: theme("colors.slate.950");
--foreground: theme("colors.slate.50");
}
body {
@apply bg-background text-foreground;
transition:
background-color 0.2s ease,
color 0.2s ease;
}
}
Key features of this setup:
suppressHydrationWarning
: Prevents hydration warnings when theme changes on clientattribute="class"
: Uses CSS classes for theme switching (.dark
class)defaultTheme="system"
: Respects user's system preferenceenableSystem
: Allows automatic system theme detectiondisableTransitionOnChange
: Prevents flash during theme transitions
Since I use TailwindCSS v4, the globals.css
might look a bit different. Another important thing to note is the use of syntax like theme("colors.slate.950")
for setting the colors. This doesn't play nice with Disqus. We will see how to fix it later.
"use client"
import React from "react"
import { DiscussionEmbed } from "disqus-react"
import { useTheme } from "next-themes"
import { useInView } from "react-intersection-observer"
interface CommentProps {
url: string
slug: string
}
export const DisqusComments = ({ url, slug }: CommentProps) => {
const { resolvedTheme = "light" } = useTheme()
const { ref, inView } = useInView({
threshold: 0,
triggerOnce: true,
fallbackInView: true,
})
const disqusShortname = "your-disqus-shortname-here"
const disqusConfig = {
identifier: slug,
url: url,
}
return (
<div ref={ref}>
{inView ? (
<div className="comments">
<DiscussionEmbed
key={resolvedTheme}
shortname={disqusShortname}
config={disqusConfig}
/>
</div>
) : null}
</div>
)
}
Key Features of this component are -
-
Lazy Loading with Intersection Observer
The component uses
react-intersection-observer
to implement lazy loading:- Comments only load when the user scrolls to the comments section.
threshold: 0
triggers loading as soon as the element enters the viewport.triggerOnce: true
ensures the component only loads once.fallbackInView: true
provides a fallback for environments where IntersectionObserver isn't available.
-
Theme-Aware Rendering
The most crucial aspect for dark mode support is the
key={resolvedTheme}
prop on theDiscussionEmbed
component. This forces React to completely remount the Disqus embed whenever the theme changes, ensuring proper color adaptation.
The component is used in the blog note page app/notes/[slug]/page.tsx
(such as this one):
<DisqusComments
slug={params.slug}
url={`${siteConfig.url}/notes/${params.slug}`}
/>
The URL construction ensures each post has a unique identifier for Disqus threads, while the slug provides a fallback identifier.
Modern CSS frameworks like Tailwind CSS v4 use OKLCH colors for better color accuracy and perceptual uniformity. However, Disqus's embedded iframe doesn't understand OKLCH color values.
When OKLCH colors are used, you'll encounter this error in the browser console:
Uncaught Error: parseColor received unparseable color: oklch(000)

This happens because Disqus validates color values and expects them to be in RGB or Hex format.

Override OKLCH colors with Hex equivalents specifically for the Disqus container:
/* Fix for https://github.com/disqus/disqus-react/issues/153 */
:root,
.comments {
background-color: #ffffff !important; /* white */
color: #0f172a !important; /* slate-950 */
}
/* Dark mode comments - using hex values */
:root,
.dark .comments {
background-color: #020617 !important; /* slate-950 */
color: #f8fafc !important; /* slate-50 */
}
- Lazy Loading: Comments only load when visible, reducing initial page load time.
- Theme Remounting: While remounting on theme change might seem expensive, it's necessary for proper theme switching and happens infrequently.
- CSS Optimization: Color fixes are applied at the CSS level, avoiding JavaScript-based solutions that could impact performance.