Build a Personal Site with ContentLayer and ChakraUI#

  • Contentlayer converts mdx, md into json and react components
  • Contentlayer does routing - nextjs prerender
  • Contentlayer appiles styles to generated components
  • Provide styles via ChakaraUI and useMDXComponent hook
  • GitHub
Build a Personal Site with Contentlayer, NextJs and Amplify

Create NextJS App#

npx create-next-app@latest my-site --typescript

add content layer

npm install contentlayer next-contentlayer

add chakraui

npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
|--.contentlayer
|--pages
|___app.tsx
|__index.tsx
|--posts
|--[slug].js
|--posts
|--post-01.mdx
|--post-02.mdx
|--post-03.mdx
|--src
|--components
|--mdx-components
|--codeblock
|--inline-code.tsx
|--linked-heading.tsx
|--mdx-component.tsx
|--contentlayer.config.js
|--next.config.js
|--package.json

configure tsconfig with paths

{
"compilerOptions": {
...
"incremental": true,
"baseUrl": "."
},
"paths": {
"contentlayer/generated": ["./.contentlayer/generated"],
"styles/*": ["styles/*"]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".contentlayer/generated"
],
"exclude": ["node_modules"]
}

configure next.config.js

const { withContentlayer } = require('next-contentlayer')
module.exports = withContentlayer({})

Configure Schema#

This is the content of contentlayer.config.js and it specifies the schema of Post. Given the schema, contentlayer can convert md, mdx content into react components.

import { defineDocumentType, makeSource } from 'contentlayer/source-files'
export const Post = defineDocumentType(() => ({
name: 'Post',
filePathPattern: `**/*.mdx`,
contentType: 'mdx',
fields: {
title: {
type: 'string',
description: 'The title of the post',
required: true
},
description: {
type: 'string'
},
date: {
type: 'date',
description: 'The date of the post',
required: true
}
},
computedFields: {
url: {
type: 'string',
resolve: post => `/posts/${post._raw.flattenedPath}`
}
}
}))
export default makeSource({
contentDirPath: 'posts',
documentTypes: [Post]
})

Configure Routes#

import { allPosts } from './../../.contentlayer/generated'
export async function getStaticPaths() {
const paths = allPosts.map(post => post.url)
return {
paths,
fallback: false
}
}

match the request url (from user) with the nextjs url using slug.

export async function getStaticProps({ params }) {
const post = allPosts.find(post => post._raw.flattenedPath === params.slug)
return {
props: {
post
}
}
}

Apply Styles#

Provide styles for generated components via MDXComponents. For example

const mdxComponents = {
h1: props => <chakra.h1 fontSize={'5xl'} apply="mdx.h1" {...props} />,
h2: props => <chakra.h2 fontSize={'3xl'} apply="mdx.h2" {...props} />
}

Then apply the styles into generated components

import React from 'react'
import { allPosts } from '.contentlayer/generated'
import { useMDXComponent } from 'next-contentlayer/hooks'
import { Box, chakra } from '@chakra-ui/react'
import styles from 'styles/Home.module.css'
const PostLayout = ({ post }) => {
const MDXContent = useMDXComponent(post.body.code)
return (
<Box bg={'gray.100'} maxW="1000px" margin={'auto'} padding="20px">
<div>
<h1 className={styles.title}>{post.title}</h1>
<MDXContent components={mdxComponents}></MDXContent>
</div>
</Box>
)
}
export default PostLayout

Amplify Hosting#

amplify init

then add hosting

amplify add hosting

select manual then publish (this work for static website). For hybrid, need to choose CI/CD pipeline with GitHub source or using vercel hosting.

amplify publish