How to Build a Table of Content from HTML in React?

How to Build a Table of Content from HTML in React?

Navigating long articles can be challenging for both users and search engines. Adding a Table of Contents (TOC) can significantly improve user experience and SEO performance. In this article, we'll explore the benefits of a TOC and guide you through creating one in React using the @wisp-cms/table-of-content package. We'll also discuss best practices for sanitizing HTML and how to manage your content with Wisp CMS.

Benefits of a TOC for SEO

Improved User Experience

A TOC helps users find the information they need quickly, reducing frustration and the likelihood of them leaving your site. By providing an overview of the article's structure, users can jump to sections of interest without scrolling through the entire content.

A well-implemented TOC can reduce bounce rates, as users are more likely to stay on a page if they can easily navigate to the parts relevant to them. This improved engagement can positively impact your site's rankings.

Increased Crawlability

Search engines benefit from a TOC as it enhances their understanding of the page's structure. A well-structured TOC makes it easier for search engines to crawl and index your content, recognizing the hierarchy and relationship between sections.

Moreover, a TOC can increase the likelihood of your site getting sitelinks in search results, leading to better visibility and higher click-through rates. When implemented correctly, a TOC adds an additional layer of internal linking, making it easier for search engines to find and rank your content.

How to Build a TOC in React using @wisp-cms/table-of-content

Installation

First, you'll need to install the @wisp-cms/table-of-content package. Use the following npm command to add it to your project:

npm install @wisp-cms/table-of-content

Generating TOC

The core function of the @wisp-cms/table-of-content package is generateTableOfContents. This function takes your HTML content as input and outputs an object containing the modified HTML and the TOC.

Here's how you can use it:

import React from 'react';
import { generateTableOfContents } from '@wisp-cms/table-of-content';

export function PostContentWithTOC({ content }: { content: string }) {
  const { modifiedHtml, tableOfContents } = generateTableOfContents(content);
  
  return (
    <div className="flex">
      <div className="prose w-full">
        <div className="blog-content" dangerouslySetInnerHTML={{ __html: modifiedHtml }} />
      </div>
      <div className="w-1/4">
        <div className="sticky top-0 max-h-screen overflow-y-auto">
          <div className="text-lg font-semibold">Table of Contents</div>
          <TableOfContents items={tableOfContents} />
        </div>
      </div>
    </div>
  );
}

The generateTableOfContents function parses your HTML content, identifies headings, and generates a TOC from them. It returns an object with the modified HTML (which now includes IDs for each heading) and an array representing the TOC.

Rendering TOC

To render the TOC, you'll need a React component that takes the TOC items and displays them in a structured format. Below is an example implementation of the TableOfContents component:

import React from 'react';
import { TableOfContentsItem } from '@wisp-cms/table-of-content';

interface TableOfContentsProps {
  items: TableOfContentsItem[];
}

export const TableOfContents: React.FC<TableOfContentsProps> = ({ items }) => {
  return (
    <nav>
      <ul>
        {items.map((item) => (
          <li key={item.id} style={{ marginLeft: `${(item.level - 1) * 20}px` }}>
            <a href={`#${item.id}`}>{item.text}</a>
          </li>
        ))}
      </ul>
    </nav>
  );
};

In this component, the items prop is an array of TOC items generated by the generateTableOfContents function. Each item is rendered as a list item with a link that points to the corresponding section in the content.

Best Practices

Sanitizing HTML

When dealing with HTML content from external sources, it's crucial to ensure that your content is safe from XSS (Cross-Site Scripting) attacks. The sanitize-html library can help you clean up potentially dangerous HTML.

First, install the sanitize-html package:

npm install sanitize-html

Then, you can use it in your component as follows:

import sanitize from 'sanitize-html';

export function PostContentWithTOC({ content }) {
  const sanitizedContent = sanitize(content, {
    allowedTags: [
      'b', 'i', 'em', 'strong', 'a', 'img', 'h1', 'h2', 'h3', 'code', 'pre', 'p', 'li', 'ul', 'ol', 'blockquote',
      'td', 'th', 'table', 'tr', 'tbody', 'thead', 'tfoot', 'small', 'div', 'iframe',
    ],
    allowedAttributes: {
      a: ['href', 'name', 'target'],
      // Add more attributes as needed
    },
  });
  const { modifiedHtml, tableOfContents } = generateTableOfContents(sanitizedContent);
  return (
    <div className="flex">
      <div className="prose w-full">
        <div className="blog-content" dangerouslySetInnerHTML={{ __html: modifiedHtml }} />
      </div>
      <div className="w-1/4">
        <div className="sticky top-0 max-h-screen overflow-y-auto">
          <div className="text-lg font-semibold">Table of Contents</div>
          <TableOfContents items={tableOfContents} />
        </div>
      </div>
    </div>
  );
}

By sanitizing the HTML content before generating the TOC, you can ensure that your application remains secure.

Using Wisp CMS to Manage Content

Introduction to Wisp CMS

Wisp CMS is a headless content management system designed for Next.js applications. It offers a range of features to simplify the process of managing and delivering content, including a user-friendly editor, image hosting, and content formatting.

Integration with Wisp CMS

Wisp CMS makes it easy to fetch content and use it in your React components. Here's how you can integrate Wisp CMS with the @wisp-cms/table-of-content package to generate and render a TOC for your content.

A quick example on how you can easily fetch your blog post using Wisp's SDK and render the post with its TOC:

// in app/wisp.tsx
import { buildWispClient } from "@wisp-cms/client";

export const wisp = buildWispClient({
  blogId: "xxx", // TODO: Replace with your blog ID
});

// in app/blog/[slug]/page.tsx
import { wisp } from "../../../wisp";

export default async function BlogPostPage({
  params: { slug },
}: {
  params: Params;
}) {
  const result = await wisp.getPost(slug);

  if (!result || !result.post) {
    redirect("/blog");
  }

  const { title, publishedAt, content, tags, image, updatedAt, author } =
    result.post;

  return (
    <div>
      {/* Other components */}
      <PostContentWithTOC content={content} />
      {/* Other components */}
    </div>
  );
}

By integrating Wisp CMS with the @wisp-cms/table-of-content package, you can efficiently manage and render content with a dynamic TOC.

Conclusion

In this article, we've explored the benefits of adding a Table of Contents (TOC) to your articles for both user experience and SEO. We've demonstrated how to use the @wisp-cms/table-of-content package to generate and render a TOC in a React application, and discussed best practices for sanitizing HTML content.

Additionally, we've highlighted how Wisp CMS can simplify content management and delivery, making it easier to maintain and update your web content. By following these guidelines and leveraging the tools discussed, you can enhance your content's readability, navigation, and search engine performance.

Ready to improve your content management and SEO? Try integrating Wisp CMS and @wisp-cms/table-of-content into your React projects today!

10/30/2024
Related Posts
How to Sanitize HTML Response from a CMS?

How to Sanitize HTML Response from a CMS?

Discover how to protect your CMS from security risks through HTML sanitization using sanitize-html library, with practical steps from the Wisp CMS blog starter kit.

Read Full Story
Seamlessly Inserting CTAs Midway into Your Blog Posts with React

Seamlessly Inserting CTAs Midway into Your Blog Posts with React

Trying to insert a component midway through a html string returned by a CMS? Use this method to seamlessly perform the insertion without breaking up words or sentences.

Read Full Story
Static Site, Dynamic Content: Powering Your NextJS Static Site with Lightweight Blog-Only CMS

Static Site, Dynamic Content: Powering Your NextJS Static Site with Lightweight Blog-Only CMS

Tired of choosing between static site performance and dynamic content? Learn how to integrate Wisp CMS with NextJS for the ultimate blogging solution. Best of both worlds awaits!

Read Full Story