Articles on: Advanced Customization

Integrating DropInBlog into a Next.js application

This guide explains how to integrate DropInBlog into your Next.js project by adding a custom component.

Overview


The integration involves creating a React component that:

Dynamically loads the DropInBlog script to embed blog content on the page.
Enhances navigation by converting DropInBlog links into client-side routes using Next.js’ router.
Observes content changes to apply routing behavior to dynamically inserted blog elements.
Renders a container <div id="dib-posts"></div> where DropInBlog will insert the blog posts.

Step-by-Step Instructions


1. Create the DropInBlog component



Create a new file dib-block.tsx in your components directory with the following code:

"use client";

import { useLayoutEffect } from "react";
import { usePathname, useSearchParams, useRouter } from "next/navigation";

/**
 * Declare DropInBlog global object on the window for TypeScript
 */
declare global {
  interface Window {
    dib?: {
      reload?: () => void;
      [key: string]: any;
    };
  }
}

/**
 * Remove all <script> or <link> tags related to DropInBlog
 */
function removeTags(tag: "link" | "script") {
  const tagList =
    tag === "script"
      ? document.querySelectorAll("script[src*='dropinblog']")
      : document.querySelectorAll("link[href*='dropinblog']");

  tagList.forEach((t) => t.remove());
}

/**
 * Create and return the DropInBlog <script> element with an onLoad callback
 */
function createDibScript(onLoad: () => void) {
  const script = document.createElement("script");
  script.src =
    "https://io.dropinblog.com/embedjs/{{blogid}}.js";
  script.async = true;
  script.onload = onLoad;
  return script;
}

/**
 * Force DropInBlog to reload content if it's already loaded
 */
function forceDibReload() {
  if (window.dib?.reload) {
    window.dib.reload();
  }
}

/**
 * Find all clickable blog elements (both <a> and <div>), and normalize <div> links by extracting href from onclick attribute
 */
function normalizeDivLinks() {
  const linksSelector = [
    "ul.dib-cat-menu>li >a",
    "a.dib-post",
    "a.dib-post-back-link",
    "a.dib-post-back-link-bottom",
    "a.dib-meta-author-link",
    "div.dib-related-posts>div.dib-related-post",
    "span.dib-meta-item.dib-post-category-text a",
  ];

  const links = document.querySelectorAll(linksSelector.join(", "));

  links.forEach((link) => {
    if (link instanceof HTMLDivElement) {
      // Extract href value from onclick="location.href='...'" and store in data-href
      let onclick = link.getAttribute("onclick") ?? "";
      onclick = onclick.replace(/location\.href\s*=\s*['"]/, "");
      onclick = onclick.replace(/['"]\s*;\s*$/, "");
      link.setAttribute("data-href", onclick);
      link.removeAttribute("onclick");
    }
  });

  return links;
}

/**
 * Add click event listeners to links (both <a> and normalized <div>s) to handle navigation using Next.js router (client-side routing)
 */
function addClickListeners(
  links: NodeListOf<Element>,
  router: ReturnType<typeof useRouter>
) {
  links.forEach((link) => {
    link.addEventListener("click", (e) => {
      e.preventDefault();

      let url: URL | null = null;

      if (link instanceof HTMLAnchorElement) {
        url = new URL(link.href);
      } else if (link instanceof HTMLDivElement) {
        const href = link.dataset?.href;
        if (href) url = new URL(href, window.location.origin);
      }

      if (!url) throw new Error("Invalid URL");

      const relativePath = url.pathname + url.search;
      router.push(relativePath);
    });
  });
}

/**
 * Normalize DropInBlog internal links and override them to use Next.js client-side routing
 */
function overrideLinkClicks(router: ReturnType<typeof useRouter>) {
  const links = normalizeDivLinks();
  addClickListeners(links, router);
}

/**
 * Main component that loads and integrates DropInBlog content into a Next.js page
 */
export default function DibBlock() {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const router = useRouter();
  const searchKey = searchParams.toString();

  useLayoutEffect(() => {
    if (pathname !== "/blog") return;

    const dibContainer = document.getElementById("dib-posts");
    if (dibContainer) dibContainer.innerHTML = "";

    removeTags("script");

    const dibScript = createDibScript(() => {
      forceDibReload();
    });

    document.head.appendChild(dibScript);

    const observer = new MutationObserver(() => {
      overrideLinkClicks(router);
    });

    if (dibContainer) {
      observer.observe(dibContainer, {
        childList: true,
        subtree: true,
      });
    }

    return () => {
      dibScript.remove();
      removeTags("link");
      observer.disconnect();
    };
  }, [pathname, searchKey, router]);

  return <div id="dib-posts"></div>;
}


Be sure to replace the {{blogid}} with what you see on the Code & Layout tab in your admin panel.

Be sure to replace /blog with the correct path if you are not using /blog

2. Use the component in a Page



import DibBlock from "@/app/components/dib-block";

export default function Blog() {
  return (
	   <DibBlock />
  );
}

Updated on: 19/06/2025