+2

Tạo blog Markdown đơn giản với Next.js

Bạn là một lập trình viên JavaScript? Bạn đang muốn xây dựng một blog cá nhân cho riêng mình nhưng chưa biết phải bắt đầu từ đâu và cần chuẩn bị những gì? Đừng quá lo lắng, hôm nay mình sẽ hướng dẫn các bạn cách xây dựng một blog có thể hiển thị bài viết bằng Markdown với Next.js

Tạo dự án bằng Next.js

Để tạo một dự án Next.js chúng ta dùng câu lệnh:

npx create-next-app@latest
# hoặc 
yarn create next-app 
# hoặc 
pnpm create next-app

Sau vài giây dự án của bạn sẽ được tạo thành công, hãy di chuyển vào trong thư mục của dự án và cài đặt thêm một vài dependencies cần thiết. yarn add -D autoprefixer postcss tailwindcss Nếu bạn muốn sử dụng Dark mode/ Ligth mode hãy cài thêm dependencies sau. yarn add next-themes Tiếp theo chúng ta cần cài đặt tailwindcss bằng cách chạy câu lệnh sau để khởi tạo các tệp tin cần thiết. yarn tailwindcss init -p Sau khi các tệp tin được tạo tự động thành công, hãy mở tệp tin tailwind.config.js và chỉnh sửa như dưới đây.

/** @type {import('tailwindcss').Config} */
module.exports = { 
  content: [
    './pages/**/*.{js,jsx}',
    './components/**/*{js,jsx}'
  ],  
  darkMode: 'class',
  theme: {
    extend: {}, 
  },  
  plugins: [], 
}

Tiếp theo đó, chúng ta cần áp dụng tailwind vào ứng dụng bằng cách chỉnh sửa tệp tin pages/_app.js

import 'tailwindcss/tailwind.css';
import { ThemeProvider } from 'next-themes';

import Navbar from './../components/Navbar';

function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider attribute="class">
      <div className="dark:bg-gray-900 bg-gray-50 w-full min-h-screen">
        <Navbar />
        <Component { ...pageProps } />
      </div>
    </ThemeProvider>
  );  
}

export default MyApp;

Kế tiếp, hãy tạo Topbar cho blog của bạn theo đường dẫn components/Navbar/index.js

import Link from 'next/link';

function Navbar () {
  return (
    <>  
      <nav className="flex items-center flex-wrap bg-gray-900 p-3">
        <Link href="/">
          <span className="text-xl text-white font-bold uppercase tracking-wide">
            Markdown Blog
          </span>
        </Link>
      </nav>
    </> 
  );  
};

export default Navbar;

Bây giờ bạn hãy truy cập http://localhost:3000 để xem chúng ta đã làm được gì nào.

Thêm Markdown vào blog

Tiếp theo, chúng ta sẽ xử lý để blog của bạn có thể biên dịch và hiển thị được nội dung viết bằng Markdown bằng cách cài đặt thêm dependencies sau. yarn add gray-matter markdown-it yarn add -D @tailwindcss/typography Tiếp theo chúng ta cần thêm typography vào trong tệp tin cấu hình tailwind tailwind.config.js

plugins: [
    require('@tailwindcss/typography')
],

Bước kế tiếp bạn hãy xóa nội dung trong tệp tin pages/index.js và thay thế bằng nội dung sau:

import fs from 'fs';
import matter from 'gray-matter';
import Image from 'next/image';
import Link from 'next/link';

export async function getStaticProps() {
  try {
    const files = fs.readdirSync('public/posts');

    const posts = files.map((fileName) => {
      const slug = fileName.replace('.md', '');
      const readFile = fs.readFileSync(`public/posts/${fileName}`, 'utf-8');
      const { data: frontmatter } = matter(readFile);

      return {
        slug,
        frontmatter
      };  
    }); 

    return {
      props: { posts }
    };  
  } catch (error) {
    console.error(error);

    return {
      props: {}
    };  
  }
};

function Blog ({ posts }) {
  return (
    <div className="grid grid-cols-1 md:grid-cols-3 lg-grid-cols-4 md:p-0 mt-8">
      {   
        posts.map(({ slug, frontmatter }) => (
          <div key={ slug } className="border border-gray-200 m-2 rounded-xl shadow-lg overflow-hidden flex flex-col">
            <Link href={ `/post/${slug}` } legacyBehaviour>
              <a>
                <Image
                  width={ 650 }
                  height={ 340 }
                  alt={ frontmatter.title }
                  src={ `/${frontmatter.socialImage}` }
                />

                <h1 className="p-4">{ frontmatter.title }</h1>
              </a>
            </Link>
          </div>
        ))
      }
    </div>
  );
};

export default Blog;

Tại đây chúng ta đã tạo một trang danh sách các bài viết đơn giản trên bố cục dạng lưới. Sử dụng getStaticProps để lấy tất cả các tệp tin bài viết trong thư mục public/posts và hiển thị thông tin lên màn hình. Nào bây giờ hãy thêm các bài đăng vào trong thư mục public/posts và ảnh lưu trữ vào thư mục public/assets/images giống ví dụ dưới đây:

title: "WebRTC For Beginners - Part 1"
metaTitle: "WebRTC For Beginners"
metaDesc: "WebRTC For Beginners"
socialImage: assets/images/webrtc.jpg
date: "2022-01-12"
tags:
    - webrtc

Chúng ta sử dụng title để hiển thị tiêu đề bài đăng và socialImage để hiển thị ảnh đại diện. Nếu không gặp trở ngại gì chúng ta sẽ được kết quả như sau:

WOW, thật tuyệt vời phải không nào! Ở bước tiếp theo chúng ta hãy xử lý khi người dùng xem chi tiết từng bài viết nhé!

Hiển thị nội dung bài đăng

Tạo một thư mục post nằm trong thư mục pages. Tại đây bạn hãy tạo tệp tin [slug].js với nội dung như sau:

import fs from 'fs';
import matter from 'gray-matter';
import md from 'markdown-it';

export async function getStaticPaths() {
  try {
    const files = fs.readdirSync('public/posts');

    const paths = files.map((fileName) => ({
      params: {
        slug: fileName.replace('.md', '') 
      }   
    }));

    return {
      paths,
      fallback: "blocking"
    };  
  } catch (error) {
    console.error(error);

    return {
      paths: [], 
      fallback: false
    };  
  }
};

export async function getStaticProps ({ params: { slug } }) {
  try {
    const fileName = fs.readFileSync(`public/posts/${slug}.md`, 'utf-8');
    const { data: frontmatter, content } = matter(fileName);

    return {
      props: {
        frontmatter,
        content
      }   
    };  
  } catch (error) {
    console.error(error);

    return {
      props: {}
    };  
  }
};

function Post ({ frontmatter, content }) {
  return (
    <div className="prose mx-auto mt-8">
      <h1>{ frontmatter.title }</h1>
      <div dangerouslySetInnerHTML={{ __html: md().render(content) }} />
    </div>
  );
};

export default Post;

Tại đây chúng ta đã biến mỗi tệp tin bài đăng chứa một url riêng biệt bằng cách sử dụng getStaticPaths, sau đó gửi qua props bằng cách sử dụng getStaticProps và hiển thị nội dung trong Post component.

Nếu bạn nhấn vào một bài viết trên trang chủ của blog sẽ thấy nội dung sau:

Tổng kết

Trong bài viết này mình đã hướng dẫn các bạn cách Tạo blog Markdown đơn giản với Next.js sử dụng Tailwind, hy vọng các bạn có thể làm theo và đạt được kết quả như mong muốn.

Các bạn có thể xem chi tiết hơn tại: https://www.thegioicode.com/2023/01/tao-blog-markdown-don-gian-voi-nextjs.html


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí