Harnessing the power of Markdown with Contentlayer and Nextjs to build a modern blog
Creating a blog is a great way to share your knowledge and expertise with others. However, setting up a full-stack blog can be daunting if you're just getting started with web development. In this article, I'll show you just how easy it is to build a full-fledged blog using some of the most popular tools available today.
We'll be using NextJS for the frontend, Contentlayer to handle Markdown content, and Tailwind CSS for styles. And for hosting? We'll deploy everything to Vercel with a single command. By the end, you'll have a fully functioning blog up and running in no time!
Why NextJS?
As a React framework, NextJS makes building powerful websites and applications incredibly quick and simple. Some key advantages for blogging include:
- Static generation - NextJS can pre-render pages at build time, providing lightning fast load times and optimizing sites for SEO. Perfect for blogs!
- File-based routing - Pages are defined as .js or .jsx files in the pages directory, keeping routing clean and intuitive.
- Image optimization - NextJS can automatically optimize images, reducing payload sizes.
- Easy deployment - Vercel integration makes deploying NextJS sites a breeze, as we'll see later!
So in summary, NextJS gives us a simple fast foundation to build our blog upon.
Enter Contentlayer
While Next.js 14 has built-in support for Markdown, we're going to take things up a notch by introducing ContentLayer into the mix. ContentLayer is a powerful content management system that seamlessly integrates with Next.js, making it a breeze to fetch and render Markdown content on your site.
With ContentLayer, you can organize your content into a structured data model, making it easier to manage and maintain your blog posts. There is no need to involve complex workflows and heavyweight dependencies that will slow down your client. Through the magic of Content Layer, all Markdown processing occurs at build time - eliminating client-side weight for maximum performance!
Contentlayer provides:
Simple Setup - Contentlayer requires just a config file and basic CLI usage. No deploying additional services.
Fast Performance - By avoiding a database or API server, Contentlayer's static site integration is faster and more affordable to run.
Lower Resource Usage - Since content is stored in files rather than a database, Contentlayer doesn't require as much memory, CPU or network bandwidth.
Easy Migration - If needs change, content can be exported to a new platform since it's stored in plain Markdown files without proprietary lock-in.
Hassle-Free Maintenance - We don't have to worry about database administration, upgrades or backups when using Contentlayer.
Soooo Let's get started!
Step 1: Installing Next.js and Tailwind
First things first, we need to set up our Next.js project. Open your terminal and run the following command to create a new Next.js app and ensure you select yes for tailwind css:
On installation, you'll see the following prompts (select accordingly):
This will create a new directory called my-blog-site
with all the necessary files and dependencies for a basic Next.js project.
Next you will navigate to your new project folder
Step 2: Installing Dependencies
Important
Before installing dependencies add this code to your package.json file:
next-contentlayer
has recently become unmaintained, this means a package upgrade was not created for nextjs14. However we can still take advantage of its features by overriding the dependency.
Your full file should look something like this:
Next, we'll install the required dependencies for our blog site. Navigate to the project directory and run the following command:
Here's what each dependency does:
next-contentlayer
: This is the core library that provides the integration between Next.js and ContentLayer.contentlayer
: This plugin allows ContentLayer to read Markdown files from your project.@tailwindcss/typography
: This plugin provides stylish typographic defaults for Markdown content.remark-gfm
: This plugin enables support for GitHub Flavored Markdown syntax in your Markdown content.
Step 3: Configuring ContentLayer
Now that we have all the dependencies installed, it's time to configure ContentLayer. Create a new file called contentlayer.config.js
in the root of your project and add the following code:
In this configuration file, we define a new document type called Blog
using defineDocumentType
. This document type represents our blog posts, and we specify that the content will be stored in Markdown files with the .mdx
extension in a folder called blog.
We define two fields for our blog posts: title
(a required string) and date
(a required date). We also define a computed field called slug
, which will be used for generating the URL paths for each blog post.
Finally, we create a new source using makeSource
, specify the contentDirPath
as 'content'
, which is where we'll store our Markdown files and set contentlayer to use the remark-gfm
plugin. We also pass the Blog
document type to the documentTypes
array.
Step 4: Adding Markdown Content
With our ContentLayer configuration in place, it's time to add some Markdown content. Create a new directory called content
in the root of your project, and inside that directory, create a new folder named blog
.
Inside the blog
folder, create a new file called my-first-post.mdx
and add the following content:
Pretty neat, right?
Notice the front matter at the top of the file, where we define the title
and date
fields for this blog post.
This is how ContentLayer knows which fields to extract from the Markdown file. You can create as many blog posts as you want by adding more *.mdx
files to the content/blog
directory.
Step 5: Rendering Blog Posts
Now that we have our Markdown content set up, it's time to render it on our Next.js site. Create a new blog
folder, inside that folder create a [slug
folder and place a page.js
file inside. The file path should look like blog/[slug]/page.js
in your src/app folder and add the following code:
In this file, we import the useMDXComponent
hook from next-contentlayer/hooks
, which allows us to render the Markdown content as React components. We also import the allBlogs
array from the contentlayer/generated
file, which contains all our blog posts.
The BlogPost
component receives the blog
prop, which contains the data for the current blog post. We use the useMDXComponent
hook to render the Markdown content of the blog post and wrap it in a div
with the prose
class from the @tailwindcss/typography
plugin. This ensures that our Markdown content is styled nicely out of the box.
The getStaticParams
function generates the paths for all the blog posts, so that Next.js can pre-render them at build time. We map over the allBlogs
array and return an array of path objects with the slug
parameter for each blog post.
Finally, the getStaticProps
function fetches the blog post data for the given slug
parameter and passes it as props to the BlogPost
component.
Step 6: Configuring Tailwind CSS, Next config, and jsconfig
To ensure that our Tailwind CSS styles are applied correctly, we need to configure it in our Next.js project. Create a new file called tailwind.config.js
in the root of your project and add the following code:
This configuration file tells Tailwind CSS to scan our pages and components for classes to include in the final CSS output. We also add the @tailwindcss/typography
and tailwindcss-markdown
plugins to enable styling for Markdown content.
Next, open the globals.css
file, delete everything and add the following lines:
This imports the Tailwind CSS base styles, components, and utilities. We also add a custom style to center images in our Markdown content.
Next, let's configure ContentLayer in your next.config.js
file, you simply need to import the withContentlayer
function and wrap your Next.js configuration with it. Here's how you would do it:
Note: You will need to rename your next.config.mjs file to next.config.js
The withContentlayer
function from the next-contentlayer
package is a higher-order function that enhances your Next.js configuration with the necessary settings to support ContentLayer.
By wrapping your nextConfig
object with withContentlayer(nextConfig)
, you're seamlessly integrating ContentLayer into your Next.js application, allowing you to use Markdown and MDX files as content sources without any additional configuration.
Finally, lets add the contentlayer generated path to you jsconfig.json
file:
Step 7: Adding Navigation and Styling
To make our blog site more user-friendly, let's add some navigation and apply some additional styling.
First, create a new folder and file called components/Layout.js
in the src/app folder and add the following code:
This Layout
component provides a basic structure for our site, including a header with navigation links, a main content area, and a footer. We use Tailwind CSS classes to style the layout.
Next, create a new file called _app.js
in the src/app folder and add the following code:
This file sets up the Layout
component as the root component for our Next.js app, ensuring that it will be rendered on every page.
Finally, we will open the file called page.js
in the app folder, delete everything and add the following code:
This Home
component displays a list of all our blog posts on the homepage. We use the allBlogs
array from ContentLayer to get the data for each blog post and render it in a grid layout using Tailwind CSS classes.
This is what your folder should look similar to this at this point
Run your project using
We're pretty much done!
Congratulations! You've successfully built a fully-fledged blog site using Next.js 14, ContentLayer, and Tailwind CSS. You've learned how to set up the project, configure ContentLayer, work with Markdown content, render blog posts, style your site with Tailwind CSS, and deploy your site to Vercel.
This is just the beginning, though. You can further enhance your blog site by adding features like commenting, author profiles, categories, and more. The beauty of using Next.js, ContentLayer, and Tailwind CSS is that they provide a solid foundation for building complex and scalable web applications.
Happy coding, and keep exploring the world of web development!