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 ( ...
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: