21
loading...
This website collects cookies to deliver better user experience
path fs gray-matter next-mdx-remote
yarn create next-app blog
cd blog
yarn add fs path gray-matter next-mdx-remote
fs | Provides a way to work with files |
---|---|
path | Provides a way to work with directories and paths. |
gray-matter | Parses the front-matter from a string or file |
next-mdx-remote | To render your mdx content on the page |
yarn add tailwindcss postcss autoprefixer -D
npx tailwindcss init -p
// tailwind.config.js
module.exports = {
mode: "jit",
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
// pages/index.js
export async function getStaticProps() {
// Read the pages/posts dir
let files = fs.readdirSync(path.join("pages/posts"));
// Get only the mdx files
files = files.filter((file) => file.split(".")[1] === "mdx");
// Read each file and extract front matter
const posts = await Promise.all(
files.map((file) => {
const mdWithData = fs.readFileSync(
path.join("pages/posts", file),
"utf-8"
);
const { data: frontMatter } = matter(mdWithData);
return {
frontMatter,
slug: file.split(".")[0],
};
})
);
// Return all the posts frontMatter and slug as props
return {
props: {
posts,
},
};
}
files = files.filter((file) => file.split(".")[1] === "mdx");
// import matter from 'gray-matter';
// Read each file and extract front matter
const posts = await Promise.all(
files.map((file) => {
// read file
const mdWithData = fs.readFileSync(
path.join("pages/posts", file),
"utf-8"
);
// extract front matter
const { data: frontMatter } = matter(mdWithData);
return {
frontMatter,
slug: file.split(".")[0],
};
})
);
posts
variable will look somethings like this -posts = {
frontMatter: {
// frontMatter object extracted from the mdx file
},
slug: string
}[]
Link
component from next to create a link to each post.// pages/index.js
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import Link from "next/link";
import PostCard from "../components/PostCard";
import Layout from "../components/Layout";
const Home = ({ posts }) => {
return (
<div className="container w-[80%] md:w-[60%] mx-auto">
<h1 className="text-blue-700 text-3xl font-bold my-12">My Blog 📙</h1>
<div className="posts md:grid md:grid-cols-3 gap-8">
{posts.map((post) => (
<Link href={`/posts/${post.slug}`} key={post.slug}>
<a>
<PostCard post={post} />
</a>
</Link>
))}
</div>
</div>
);
};
export default Home;
export async function getStaticProps() {
// Read the pages/posts dir
let files = fs.readdirSync(path.join("pages/posts"));
// Get only the mdx files
files = files.filter((file) => file.split(".")[1] === "mdx");
// Read each file and extract front matter
const posts = await Promise.all(
files.map((file) => {
const mdWithData = fs.readFileSync(
path.join("pages/posts", file),
"utf-8"
);
const { data: frontMatter } = matter(mdWithData);
return {
frontMatter,
slug: file.split(".")[0],
};
})
);
// Return all the posts frontMatter and slug as props
return {
props: {
posts,
},
};
}
const PostCard = ({ post }) => {
return (
<div className="rounded-md w-72 border transition-all hover:text-blue-700 hover:shadow-lg hover-scale:105 cursor-pointer">
<img src={post.frontMatter.cover_image} alt="Cover Image" />
<div className="mt-2 p-2">
<h2 className="font-semibold text-xl">{post.frontMatter.title}</h2>
</div>
</div>
);
};
export default PostCard;
export async function getStaticPaths() {
// Read the files inside the pages/posts dir
const files = fs.readdirSync(path.join("pages/posts"));
// Generate path for each file
const paths = files.map((file) => {
return {
params: {
slug: file.replace(".mdx", ""),
},
};
});
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params: { slug } }) {
// read each file
const markdown = fs.readFileSync(
path.join("pages/posts", slug + ".mdx"),
"utf-8"
);
// Extract front matter
const { data: frontMatter, content } = matter(markdown);
const mdxSource = await serialize(content);
return {
props: {
frontMatter,
slug,
mdxSource,
},
};
}
// pages/posts/[slug.js]
import path from "path";
import matter from "gray-matter";
import { serialize } from "next-mdx-remote/serialize";
import { MDXRemote } from "next-mdx-remote";
import styles from "../../styles/Post.module.css";
const Post = ({ frontMatter, slug, mdxSource }) => {
return (
<Layout title={frontMatter.title}>
<div className={styles.post}>
<h1 className="font-semibold my-8 text-3xl text-blue-700">
{frontMatter.title}
</h1>
<MDXRemote {...mdxSource} />
</div>
</Layout>
);
};
// styles/Post.module.css
.post {
@apply container w-[90%] md:w-[60%] mx-auto my-12;
}
.post p {
@apply leading-7 my-4;
}
.post img {
@apply my-4 w-full;
}