Why do you need one?
Not only does having a blog website help you to establish your authority and expertise, but it also allows you to connect with like-minded individuals and build a community around your content. Additionally, a blog website can be a valuable tool for driving traffic to your other online platforms, such as social media profiles or business websites.
Let's start
We will be using mdx, contentlayer and nextjs 13 for this tutorial.
Mdx - Allows you to write markdown content with custom react components
Contentlayer - Contentlayer is the glue between your content and your code.
Nextjs 13 - Out of the box Seo and performance
Project setup
First, let's create a new Next.js application, you can follow the official guide here. I will be using npm
for this, but you can use yarn
or pnpm
as well.
- Create a new Next.js project.
npx create-next-app contentlayer-example
cd contentlayer-example
Install Dependencies
npm install contentlayer next-contentlayer
To hook Contentlayer into the next dev
and next build
processes, you'll want to wrap the Next.js configuration using the withContentlayer
method. In the nextjs.config.js
add this code.
const { withContentlayer } = require("next-contentlayer")
/** @type {import('next').NextConfig} */
const nextConfig = {
// your nextjs config
...
}
module.exports = withContentlayer(nextConfig)
Now add these lines to your jsconfig.json
or tsconfig.json
if using typescript
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"contentlayer/generated": ["./.contentlayer/generated"]
}
},
"include": ["next-env.d.ts", "**/*.tsx", "**/*.ts", ".contentlayer/generated"]
}
Contentlayer generates files in the .contentlayer/generated
directory. This tells JavaScript (or TypeScript) to create an alias of contentlayer/generated
to the generated files directory.
Now Define Schemas
In the root create a file contentlayer.config.js
and define a schema for your blog post.
export const Post = defineDocumentType(() => ({
name: "Post",
filePathPattern: `blog/**/*.mdx`,
contentType: "mdx",
fields: {
title: {
type: "string",
required: true,
},
description: {
type: "string",
}
},
computedFields: {
slug: {
type: "string",
resolve: (doc) => `/${doc._raw.flattenedPath}`,
},
slugAsParams: {
type: "string",
resolve: (doc) => doc._raw.flattenedPath.split("/").slice(1).join("/"),
},
},
}))
export default makeSource({
contentDirPath: "blogs",
documentTypes: [Page, Doc, Guide, Post, Author],
})
This configuration specifies a single document type called Post
. These documents are expected to be .mdx
files that live within a blogs
directory in your project. And data objects generated from these files will have the following properties:
title, description
: String pulled from the file's frontmatter.body
: An object that contains theraw
content from the markdown file and the convertedhtml
string. (This is built into Contentlayer by default and does not have to be defined.)slug
: string defining the whole URL of the content- eg. /blogs/make-your-own-site
slugAsParams
: string defining slug as params- eg. /make-your-own-site
you can add more fields to your blogs by defining them in the
fields
object
Add blog content
Create a few markdown files in a blogs
directory and add some content to those files. Here's an example of what a post file at posts/build-your-site.mdx
might look like:
---
title: Lorem Ipsum
date: 2021-12-24
---
Your Content
you can add react components here as well (later in this blog)
Render it to UI
Now everything is set up and the content is generated in ./contentlayer/generated
and you just need to show it.
Copy the code from mdx.ts file here and paste it into a mdx-components.js
. the code contains basic mdx elements you can extend it with your custom react ui components as well.
// [...slug].js
import { Mdx } from "@/components/markdown/mdx-components";
import { allBlogs } from "@/.contentlayer/generated";
async function getDocFromParams(params) {
const slug = params.slug?.join("/") || "";
const blog = allBlogs.find((blog) => blog.slugAsParams === slug);
if (!blog) {
null;
}
return blog;
}
const page = async() => {
const blog = await getDocFromParams(params);
return (
<div className="mx-auto w-full min-w-0">
<title>{post.title}</title>
<p>{blog.description}</p>
<Mdx code={blog.body.code} />
</div>
)
}
Here are some more customization and styling
you can copy-paste files from here and add it your project to make your mdx look as you want
That's all
Now you have your own site with custom mdx customizations.