My current setup includes an NX monorepo with Next.js apps and libraries. I was contemplating how to establish a blog that balances convenience with simplicity. Then, I recalled MDX—a markdown format that supports components, offering a simple yet rich solution for a mostly static blog.
I created a Next.js app using NX.
next.config.js
First, install the MDX package:
npm i @next/mdx
Then, integrate the withMDX
plugin:
const withMDX = require('@next/mdx')();
Create an articles
directory within src
to house your markdown articles.
Sample article content:
--- title: 'Nextjs 14 MDX Blog' date: '2024-04-15' updated: '2024-04-15' --- # Where am I starting from? From here
Next I added global.css
with this:
pre:has(> code) { background-color: #f5f5f5; padding: 1rem; border-radius: 0.5rem; } :not(pre) > code { background-color: #f5f5f5; padding: 0.25rem; border-radius: 0.25rem; }
npm i gray-matter react-markdown
Since we no longe have getStaticPaths
we must use generateStaticParams
const generateStaticParams = async () => { const slugs = await getArticleSlugs(); return slugs.map(async ({ slug }) => ({ params: { slug, }, })); };
Never used it before, but its a funny setup.
I needed 2 methods:
getArticleSlugs
const getArticleSlugs = async () => { const articlesDirectory = path.join(process.cwd(), 'src/articles'); const files = fs.readdirSync(articlesDirectory); const articles = files.map((filename) => { const slug = filename.replace('.mdx', ''); return { slug }; }); return articles; };
just to read the slugs at build time, and getArticleBySlug
, to retrieve the whole article.
const getArticleBySlug = async (slug: string) => { const articlesDirectory = path.join(process.cwd(), 'src/articles'); const filePath = path.join(articlesDirectory, `${slug}.mdx`); const markdownWithMetadata = fs.readFileSync(filePath, 'utf-8'); const { data: frontMatter, content } = matter(markdownWithMetadata); const article = { frontMatter, slug, href: `/article/${slug}`, content, }; return article; };
Then you have to stitch it together in the actual page
... const ArticlePage = async (props: ArticleProps) => { const { frontMatter, content } = await getArticleBySlug( props.params.slug ); return ( ...
For the root of the blog you just need to get some of the data from each article, like for example:
const getArticles = async () => { const articlesDirectory = path.join(process.cwd(), './src/articles'); const files = await fs.readdirSync(articlesDirectory); const articles = await files.map((filename) => { const filePath = path.join(articlesDirectory, filename); const markdownWithMetadata = fs.readFileSync(filePath, 'utf-8'); const { data: frontMatter } = matter(markdownWithMetadata); const slug = filename.split('.')[0]; return { frontMatter, slug, href: `/article/${slug}`, }; }); return articles; };
And just use it from the page component
... const Home = async () => { const articles = await getArticles(); return ( ...
Learn how to enhance your code snippets with a COPY button in your syntax highlighter. This guide provides a step-by-step solution to implement a user-friendly copy feature, improving code sharing and readability.
Discover the magic behind automated article generation using OpenAI's GPT-4o and how it’s revolutionizing content creation.
Learn how to implement a pre-commit hook in TypeScript that validates and grades content quality using AI, specifically GPT-4o, applies changes, and manages state with markdown metadata.
We use cookies to improve your experience and analyze our traffic. By using our site, you consent to our use of cookies. You can manage your preferences below: