Published on 8/7/2024

Rendering HTML Content for Blogs in Next.js: A Comprehensive Guide

In this guide, we'll explore how to set up HTML rendering for blog content in Next.js, moving away from Markdown to a more flexible HTML-based approach. This method offers greater control over styling and structure while maintaining the ease of content management.

Why HTML Instead of Markdown?

While Markdown is popular for its simplicity, HTML provides more flexibility and control over your content's structure and styling. Here are some advantages:

  1. Rich formatting and complex layouts
  2. Direct integration of custom components
  3. Easier implementation of dynamic content
  4. Better control over SEO elements

Setting Up the Project

Step 1: Define Your Blog Data Structure

Create a file (e.g., article.ts) to define your blog post structure:


export const blogs = [
  {
    id: 0,
    name: "Data Fetching in Next.js 14",
    description: "Learn how to fetch data in Next.js 14",
    title: "Data Fetching In Next.js 14",
    slug: "nextjs-data-fetch",
    created_at: new Date("8/1/2024").toLocaleString(),
    link: "/blog/nextjs-data-fetch",
    article: data_fetching_article, // This will be your HTML content
    image: "https://example.com/image.png",
    keywords: []
  },
  // Add more blog posts here
];

// In a separate file, define your HTML content
export const data_fetching_article = `
  <h1>Data Fetching in Modern Web Applications</h1>
  <p>Data fetching is a crucial aspect of building modern web applications...</p>
  <!-- Rest of your HTML content -->
`;
  

Step 2: Create Utility Functions

Set up functions to handle blog data:


// getAllBlogSlugs.ts
export function getAllBlogSlugs() {
  return blogs.map(blog => ({
    params: { slug: blog.slug || blog.name.toLowerCase().replace(/ /g, '-') }
  }));
}

// getBlogs.ts
export function getBlogs(slugs: string) {
  if (!slugs || slugs.length === 0) {
    return blogs;
  }

  return blogs.filter(blog =>
    slugs.includes(blog.slug) || slugs.includes(blog.name.toLowerCase().replace(/ /g, '-'))
  );
}
  

Step 3: Set Up the Blog Page Component

Create a dynamic page for rendering individual blog posts:


import { getBlogs } from '@/content/functions/get-blog-slug';
import ReactHtmlParser from 'react-html-parser';

export async function generateMetadata({ params }: { params: { slug: string } }) {
  const response = getBlogs(params?.slug);
  // ... metadata setup
}

export async function generateStaticParams() {
  return blogs.map(blog => ({
    params: { slug: blog.slug || blog.name.toLowerCase().replace(/ /g, '-') }
  }));
}

export default async function Blog({ params }: { params: { slug: string } }) {
  const response = await getBlogs(params?.slug);

  return (
    <article className="container relative max-w-3xl mt-[4rem]">
      {/* ... other content */}
      <div className='mt-[1rem]'>
        {ReactHtmlParser((response?.[0]?.article), {
          transform: transformNode
        })}
      </div>
      {/* ... other content */}
    </article>
  );
}

const transformNode = (node: any) => {
  // Add classes to different HTML elements
  if (node.type === "tag" && node.name === "p") {
    let className = "leading-7 text-sm mt-6";
    // ... more class assignments
  }
  // ... handle other tags
};
  

Step 4: Create a Blog Card Component

For displaying blog previews:


import Link from 'next/link'
import BlogCard from './blog-card'
import { blogs } from '@/content/article'

export default function BlogCardSection() {
  return (
    <div className='max-w-[700px] my-[2rem] w-full'>
      <div className='grid sm:grid-cols-2 lg:grid-col:2 xl:grid-cols-2 gap-2'>
        {blogs?.map((blog) => (
          <Link href={blog?.link} key={blog?.id}>
            <BlogCard title={blog?.name} description={blog?.description} image={blog?.image} />
          </Link>
        ))}
      </div>
    </div>
  );
}
  

Key Benefits of This Approach

  1. Flexibility: HTML allows for more complex layouts and styling compared to Markdown.
  2. Dynamic Content: Easily integrate dynamic elements or custom React components within your blog content.
  3. SEO Optimization: Greater control over HTML structure for better SEO practices.
  4. Consistency: Apply consistent styling across all blog posts using the transformNode function.
  5. Performance: Static generation of blog pages for faster load times.

Conclusion

By rendering blog content as HTML in Next.js, you gain more control over your blog's appearance and functionality. This approach combines the benefits of static site generation with the flexibility of dynamic content, resulting in a powerful and efficient blogging system.

Remember to keep your HTML content well-structured and use semantic tags for the best results in terms of accessibility and SEO. Happy blogging!