Coding, Insights, and Digital Discoveries ๐ฉ๐ปโ๐ป
From Stay-at-Home Mom to a Developer After 50
As 2024 came to a close, I took some time during the holiday season to reflect, recharge, and reimagine my blog. Iโm thrilled to share one of the exciting new additions Iโve been working onโa Sitemap Page! ๐ This sitemap page isnโt for search engines to crawl, but solely for you, my readers, to easily navigate my blog. Itโs a single page where every blog post title is displayed in chronological order, making it easier than ever for readers to explore, revisit, or catch up on content that might have been missed.
Working on the new sitemap page wasnโt just about organizing contentโit was also an opportunity to deepen my understanding of some crucial web development concepts:
These concepts are at the heart of modern web development, especially when using frameworks like Next.js
, and they play a significant role in website performance, user experience, and SEO. Letโs break them down in a way thatโs easy to understand, even if youโre just starting out with Next.js! ๐
In Client-Side Rendering (CSR), the browser downloads a minimal HTML page and then uses JavaScript to fetch data and render the content dynamically. Think of it like ordering a pizza ๐: you place the order (fetch the data), wait for it to arrive (load the content), and then enjoy it (see the page).
Letโs take my sitemap page as an example. The sitemap page lists all blog post titles, grouped by month in descending order, with each title linking to its respective post page.
In CSR, JavaScript (typically with fetch
or axios
) is used to retrieve the data from an API or a local JSON file. Hereโs how you might fetch the data in a Next.js component (without using Contentlayer2):
import { useEffect, useState } from 'react';
const SitemapPage = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
// Fetch data from your API or local file
fetch('/api/posts')
.then((response) => response.json())
.then((data) => {
// Sort posts by date in descending order
const sortedPosts = data.sort((a, b) => new Date(b.date) - new Date(a.date));
setPosts(sortedPosts);
})
.catch((error) => console.error('Error fetching posts:', error));
}, []);
// Group posts by month
const groupedPosts = posts.reduce((acc, post) => {
const month = new Date(post.date).toLocaleString('default', { month: 'long', year: 'numeric' });
if (!acc[month]) {
acc[month] = [];
}
acc[month].push(post);
return acc;
}, {});
return (
<div>
<h1>Sitemap</h1>
{Object.entries(groupedPosts).map(([month, posts]) => (
<div key={month}>
<h2>{month}</h2>
<ul>
{posts.map((post) => (
<li key={post.slug}>
<a href={`/posts/${post.slug}`}>{post.title}</a>
</li>
))}
</ul>
</div>
))}
</div>
);
};
export default SitemapPage;
Once the data is fetched, itโs stored in the componentโs state (posts
). The component then groups the posts by month and renders them in a list. Each post title is a link to the corresponding post page.
Pros:
Cons:
While CSR is great for dynamic, interactive apps, itโs not always the best choice for performance. Letโs explore why Server-Side Rendering (SSR) and Static Site Generation (SSG) are often better options:
With SSR, the server fetches the data and renders the HTML on each request before sending it to the client. Think of it like a chef ๐งโ๐ณ preparing your meal in the kitchen and serving it ready-to-eat.
TIP
SSG generates the HTML at build time and reuses it for each request. Imagine a vending machine ๐ฅค: the snacks are already prepared and ready to grab. This is exactly what Contentlayer2 does for blogging websites.
TIP
For my blog, I use Contentlayer2
for content management. It generates a local file (allBlogs
) at build time, which includes all the blog post metadata (titles, slugs, dates). Since the Next.js App Router defaults to SSG
, the sitemap page is automatically pre-rendered. This is perfect because the list of articles doesnโt change frequently. ๐
import { allBlogs } from "contentlayer/generated";
import Link from "next/link";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Site Map",
description: "Browse articles organized by month.",
};
export default function SitemapPage() {
const articlesByMonth = allBlogs
.filter((post) => !post.draft)
.reduce((acc, post) => {
const date = new Date(post.date);
const month = date.toLocaleString("default", { month: "long" });
const year = date.getFullYear();
const key = `${month} ${year}`;
if (!acc[key]) acc[key] = [];
acc[key].push(post);
return acc;
}, {} as Record<string, typeof allBlogs>);
const sortedMonths = Object.keys(articlesByMonth).sort((a, b) => {
const [monthA, yearA] = a.split(" ");
const [monthB, yearB] = b.split(" ");
const dateA = new Date(`${monthA} 1, ${yearA}`);
const dateB = new Date(`${monthB} 1, ${yearB}`);
return dateB.getTime() - dateA.getTime();
});
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">Site Map</h1>
{sortedMonths.map((month) => (
<div key={month} className="mb-6">
<h2 className="text-xl font-semibold">{month}</h2>
<ul className="list-disc ml-6">
{articlesByMonth[month].map((post) => (
<li key={post._id}>
<Link
href={`/${post.path}`}
className="text-gray-600 hover:underline"
>
{post.title}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
);
}
Use SSR When:
Use SSG When:
Sometimes, the best approach is a mix of SSR and SSG. For example:
This hybrid approach ensures optimal performance, scalability, and flexibility for all kinds of websites. ๐
After diving deep into the comparison of CSR, SSR, and SSG, I gained a newfound appreciation for how Contentlayer2 is structured and its incredible functionality. ๐ Contentlayer2 works behind the scenes to generate blog posts at build time, seamlessly converting markdown content into HTML. This process ensures that my blog is not only fast and efficient but also optimized for SEO. ๐
Building the sitemap page was more than just a technical taskโit was a journey of discovery. I learned the strengths and weaknesses of CSR, SSR, and SSG, and how to choose the right approach for different use cases. But most importantly, I gained a deeper understanding of how Contentlayer2 powers my blog, making it fast, efficient, and optimized for search engines. ๐
Thank you for joining me on this journey, and I hope this article helps you make informed decisions about rendering strategies in your Next.js projects. Happy coding, and see you in the next post! ๐๐