Reputation: 161
I have published my code in github if anyone wants to clone it. i also uploaded in code sandbox.
Basically i wanted to create a blog with nextjs with md or mdx and so i looked at the nextjs docs. i saved some mdx files in the blogposts folder and tried two ways to render the blogs on the website like the docs say:
/example
route)Here's the behavior I've observed:
In the first approach (/example), the MDX content renders correctly, and some plugins like rehype-pretty-code work, but others, like remark-toc, do not. Additionally, custom imported React components do not function; only Markdown is rendered. In the second approach (/posts/test), none of the plugins function correctly, including rehype-pretty-code.
Below are the relevant parts of my code setup:
mdx-components.tsx
import type { MDXComponents } from "mdx/types";
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
};
}
next.config.mjs
import createMDX from "@next/mdx";
import remarkRehype from "remark-rehype";
import remarkToc from "remark-toc";
import rehypeSanitize from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
const nextConfig = {
pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
};
// Use dynamic import for rehype-highlight
const withMDX = async () => {
return createMDX({
options: {
remarkPlugins: [remarkToc, remarkRehype],
rehypePlugins: [
rehypeSanitize,
rehypeStringify,
rehypeAutolinkHeadings,
[
rehypePrettyCode,
{
theme: "one-dark-pro",
},
],
],
},
})(nextConfig);
};
export default withMDX();
app/posts/[slug]/page.tsx
import getFormattedDate from "@/lib/getFormattedDate";
import { getBlogPosts } from "@/lib/posts";
import Link from "next/link";
import { notFound } from "next/navigation";
export function generateStaticParams() {
let posts = getBlogPosts();
return posts.map(async (post) => ({
slug: (await post).slug,
}));
}
export default async function Post({ params }: { params: { slug: string } }) {
let post = getBlogPosts().find(
async (post) => (await post).slug === params.slug
);
if (!post) {
notFound();
}
return (
<>
<main>
<h1 className="title font-semibold text-2xl tracking-tighter">
{(await post).metadata.title}
</h1>
<div className="flex justify-between items-center mt-2 mb-8 text-sm">
<p className="text-sm text-neutral-600 dark:text-neutral-400">
{getFormattedDate((await post).metadata.publishedAt)}
</p>
</div>
<article
className="prose prose-slate"
dangerouslySetInnerHTML={{ __html: (await post).content }}
></article>
<Link href="/">← Back to home</Link>
</main>
</>
);
}
and finally the code used to read the blogposts mdx files:
/lib/posts.ts
import fs from "fs";
import path from "path";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import remarkFrontmatter from "remark-frontmatter";
import remarkToc from "remark-toc";
import rehypeSanitize from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import html from "remark-html";
import matter from "gray-matter";
export function getBlogPosts() {
const dir = path.join(process.cwd(), "blogposts");
const mdxFiles = fs
.readdirSync(dir)
.filter((file) => path.extname(file) === ".mdx");
const blogPosts = mdxFiles.map(async (file) => {
const filePath = path.join(dir, file);
const fileContents = fs.readFileSync(filePath, "utf-8");
// Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents);
const processedContent = await unified()
.use(html)
.use(remarkParse)
.use(remarkFrontmatter)
.use(remarkRehype)
.use(rehypePrettyCode, {
theme: "one-dark-pro",
}) // Prettify code blocks
.use(rehypeAutolinkHeadings) // Add anchor links to headings
.use(remarkToc) // Generate table of contents
.use(rehypeSanitize) // Sanitize HTML input
.use(rehypeStringify)
.process(matterResult.content);
const content = processedContent.toString();
let slug = path.basename(file, path.extname(file));
return {
metadata: matterResult.data,
slug,
content,
};
});
return blogPosts;
}
Has anyone faced similar issues or knows how to resolve the plugin and rendering problems in these scenarios? Its the first time i am using these libraries and i am not familiar with many of the plugins, if you guys think many of the plugins are not nescessary please tell me :). Additionally, as I plan to manage the blogs through a database, can the getBlogPosts() function be adapted to fetch from a database instead of reading from files without significant changes?
Thank you a lot for your help.
Upvotes: 0
Views: 433
Reputation: 21
I am not sure if this is the underlying issue, but the getBlogPosts
declaration is missing the async
keyword.
I checked the repo and found the usage of this function
let post = getBlogPosts().find(
async (post) => (await post).slug === params.slug
);
and it seems like Array.find()
doesn't work with an async callback.
See Using an async function in Array.find()
Upvotes: 0