I'm writing adding a blog to my website and I'm strugging with App Router/React Server Component restrictions.
Currently my initial /blog/page.tsx displays this
import path from 'path';
import matter from 'gray-matter';
import BlogLayout, { BlogTitle, BlogDate, BlogContent } from './layout/BlogLayout';
import { unified } from 'unified';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeDocument from 'rehype-document';
import rehypeFormat from 'rehype-format';
import remarkParse from 'remark-parse';
import Link from 'next/link';
interface PreviousPost {
title: string;
date: string;
slug: string;
}
interface Post {
title: string;
date: string;
content: string;
}
async function getPreviousPosts(
directoryPath: string
): Promise<PreviousPost[]> {
const files = await fs.readdir(directoryPath);
const markdownFiles = files.filter(
file => file.endsWith('.md') && file !== '.DS_Store'
);
const sortedFiles = markdownFiles.sort((a, b) => b.localeCompare(a)).slice(1);
const previousPosts = await Promise.all(
sortedFiles.map(async filename => {
const filePath = path.join(directoryPath, filename);
const fileContents = await fs.readFile(filePath, 'utf8');
const { data } = matter(fileContents);
const slug = filename.replace(/\.md$/, '');
return {
title: data.title as string,
date: data.date as string,
slug,
};
})
);
return previousPosts;
}
async function getMostRecentMarkdownFile(
directoryPath: string
): Promise<string> {
const files = await fs.readdir(directoryPath);
// Assuming files are named in a way that allows lexical sorting, e.g., YYYY-MM-DD-title.md
const sortedFiles = files.sort((a, b) => b.localeCompare(a));
return path.join(directoryPath, sortedFiles[0]);
}
async function readMarkdownFile(filePath: string): Promise<Post> {
const fileContents = await fs.readFile(filePath, 'utf8');
const { data, content } = matter(fileContents);
const processedContent = await unified()
.use(remarkParse)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeDocument)
.use(rehypeFormat)
.use(rehypeStringify)
.process(content);
return {
title: data.title,
date: data.date,
content: processedContent.toString(),
};
}
export default async function BlogPost() {
const postsDirectory = path.join(process.cwd(), 'src/app/blog/_posts');
const mostRecentFilePath = await getMostRecentMarkdownFile(postsDirectory);
const mostRecentPost: Post = await readMarkdownFile(mostRecentFilePath);
const previousPosts = await getPreviousPosts(postsDirectory);
return (
<div>
<BlogLayout>
<div>
<div>
<div>
<BlogTitle>{mostRecentPost.title}</BlogTitle>
<BlogDate>{mostRecentPost.date}</BlogDate>
<BlogContent htmlContent={mostRecentPost.content} />
</div>
</div>
<div>
<h3>
Previous Posts
</h3>
<ul>
{previousPosts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>
<div>{post.title}</div>
<div>{post.date}</div>
</Link>
</li>
))}
</ul>
</div>
</div>
</BlogLayout>
</div>
);
}
as you can see it does a couple of things, it getMostRecentMarkdownFile and uses that to display the current content.
I then also getPreviousPost and use that to map a list of links
<Link href={`/blog/{$post.slug}`} /> which are then opened in /blog/[slug]/page.tsx.
I want this new page to be an identical copy, but instead of getMostRecentMarkdownFile I want to display whichever file was used to create the slug from the link.
I'm bashing my head against a wall because I can't use getStaticProps or anything like that since I'm using the App Router.
I would absolutely appreciate some guidance with this.
Note: Forgive all of the divs, I removed tailwind classes for cleanliness.
I don't even know where to being, I've been trying everything I can think of for 3 days. I've even tried throwing it at GPT, but its no help as its not well versed in App Router/RSC - it even tries telling me to use React.FC.