Lets build a Markdown blog using Next.js, Shadcn, Tailwind, Pieces, Remark & Rehype 🔥
Building a Markdown blog using **Next.js**, **Shadcn**, **Tailwind CSS**, **Pieces**, **Remark**, and **Rehype** is a fantastic idea! This stack will give you a modern, performant, and highly customizable blog. Let’s break it down step by step.
---
### **1. Set Up Next.js Project**
Start by creating a new Next.js project:
```bash
npx create-next-app@latest my-markdown-blog
cd my-markdown-blog
```
---
### **2. Install Tailwind CSS**
Install Tailwind CSS and configure it:
```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
Update `tailwind.config.js`:
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
};
```
Add Tailwind to your `globals.css`:
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
---
### **3. Add Shadcn Components**
Shadcn is a collection of reusable components built on top of Tailwind CSS. Install it:
```bash
npx shadcn-ui@latest init
```
Follow the prompts to configure Shadcn. Then, add components as needed:
```bash
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
```
---
### **4. Set Up Markdown Processing**
To process Markdown files, we’ll use **Remark** and **Rehype**.
Install the required packages:
```bash
npm install remark remark-html remark-prism rehype-highlight gray-matter
```
- `remark`: Core Markdown processor.
- `remark-html`: Converts Markdown to HTML.
- `remark-prism`: Adds syntax highlighting with Prism.
- `rehype-highlight`: Alternative syntax highlighter.
- `gray-matter`: Parses front matter from Markdown files.
---
### **5. Create a Markdown Utility**
Create a utility function to parse Markdown files. Add a file `lib/markdownToHtml.js`:
```javascript
import { remark } from 'remark';
import html from 'remark-html';
import prism from 'remark-prism';
import matter from 'gray-matter';
export async function markdownToHtml(markdown) {
const result = await remark()
.use(html)
.use(prism)
.process(markdown);
return result.toString();
}
export function parseMarkdown(content) {
const { data, content: markdown } = matter(content);
return { data, markdown };
}
```
---
### **6. Fetch and Display Markdown Posts**
Create a folder `_posts` in the root directory and add some Markdown files with front matter:
```markdown
---
title: "My First Post"
date: "2023-10-01"
---
# Hello, World!
This is my first blog post.
```
Create a page to list all posts (`pages/index.js`):
```javascript
import fs from 'fs';
import path from 'path';
import Link from 'next/link';
import { parseMarkdown } from '../lib/markdownToHtml';
export async function getStaticProps() {
const postsDirectory = path.join(process.cwd(), '_posts');
const filenames = fs.readdirSync(postsDirectory);
const posts = filenames.map((filename) => {
const filePath = path.join(postsDirectory, filename);
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data } = parseMarkdown(fileContent);
return {
slug: filename.replace(/\.md$/, ''),
...data,
};
});
return { props: { posts } };
}
export default function Home({ posts }) {
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold mb-4">Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.slug} className="mb-2">
<Link href={`/posts/${post.slug}`}>
<a className="text-blue-500 hover:underline">{post.title}</a>
</Link>
</li>
))}
</ul>
</div>
);
}
```
---
### **7. Create Dynamic Post Pages**
Create a dynamic route for individual posts (`pages/posts/[slug].js`):
```javascript
import fs from 'fs';
import path from 'path';
import { markdownToHtml, parseMarkdown } from '../../lib/markdownToHtml';
export async function getStaticPaths() {
const postsDirectory = path.join(process.cwd(), '_posts');
const filenames = fs.readdirSync(postsDirectory);
const paths = filenames.map((filename) => ({
params: { slug: filename.replace(/\.md$/, '') },
}));
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
const filePath = path.join(process.cwd(), '_posts', `${params.slug}.md`);
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data, markdown } = parseMarkdown(fileContent);
const content = await markdownToHtml(markdown);
return { props: { post: { ...data, content } } };
}
export default function Post({ post }) {
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold mb-4">{post.title}</h1>
<div
className="prose prose-lg"
dangerouslySetInnerHTML={{ __html: post.content }}
/>
</div>
);
}
```
---
### **8. Add Syntax Highlighting**
To enable syntax highlighting, update your `tailwind.config.js`:
```javascript
module.exports = {
content: [
// ...existing content
],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/typography')],
};
```
Then, wrap your Markdown content with the `prose` class (as shown in the `Post` component above).
---
### **9. Add Pieces for Code Snippets**
If you want to integrate **Pieces** for managing code snippets, you can use their API or SDK. Install the Pieces client:
```bash
npm install @pieces-app/client
```
Use it to fetch and display code snippets in your blog posts.
---
### **10. Deploy**
Deploy your blog to **Vercel** or any other platform:
```bash
vercel
```
---
### **Final Notes**
- Customize the design using Tailwind and Shadcn components.
- Add more features like tags, categories, or a search bar.
- Optimize performance with Next.js features like `getStaticProps` and `getStaticPaths`.
Let me know if you need help with any specific part! 🚀
Comments
Post a Comment