Membuat Blog dengan NextJs TypeScript dan TailwindCSS Part II

Membuat Blog dengan NextJs TypeScript dan TailwindCSS Part II

Categories: tutorial-id
Tags: blognextjstypescripttailwindcssmarkdownwebsite
ā† Back to home

Sambungan Part I

Pada bagian sebelumnya kita sudah berhasil melakukan requirement minimal dan berhasil menjalankan blog yang kita buat. Pada bagian ini kita akan membahas tentang menulis, membaca dan menampilkan artikel.

Contoh Artikel

Buatlah direktori 'posts/' pada root directory project Anda. Salinlah artikel di bawah ini dan simpan ke 'posts/contoh-1.md'

---
title: 'Contoh Artikel'
date: '2022-06-01'
---

Ini adalah contoh pertama sebuah artikel blog yang disimpan dalam format **markdown**.

Kemudian, salin pula artikel di bawah ini dan simpan ke 'posts/contoh-2.md'

---
title: 'Artikel kedua'
date: '2022-05-22'
---

### Link Terkait
- [NextJs](https://nextjs.org/)
- [TypeScript](https://www.typescriptlang.org/)

Persiapan

Selanjutnya kita akan merubah file 'pages/index.tsx' untuk melakukan:

  • parse setiap file markdown dan mendapatkan 'title', 'date' dan nama file (akan digunakan sebagai 'id' untuk URL artikel)
  • membuat daftar artikel pada halaman index, dan mensortir berdasarkan 'date'

Untuk itu kita memerlukan mengimplementasikan 'getStaticProps'.

Pertama-tama, kita perlu melakukan instalasi

  • 'gray-matter' untuk melakukan parsing metadata dari setiap file markdown. Ketikkan perintah berikut pada terminal VSCode Anda.
  • 'remark' dan 'remark-html' untuk mengkonversi konten file markdown dari gray-matter menjadi string HTML.
yarn add gray-matter
yarn add remark
yarn add remark-html

Membuat Library

Selanjutnya, kita akan membuat library untuk mendapatkan 'fetch' data dari sistem file.

  • Buat sebuah direktori 'lib' pada root direktori
  • Buat file 'lib/posts.tsx' dan isi dengan kode berikut
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { remark } from 'remark'
import html from 'remark-html'

const postsDirectory = path.join(process.cwd(), 'posts')

// if count parameter exist then return count of posts
export function getSortedPostsData(count?: number) {
  // Get file names under /posts
  const fileNames = fs.readdirSync(postsDirectory)
  const allPostsData = fileNames.map(fileName => {
    // Remove ".md" from file name to get id
    const id = fileName.replace(/\.md$/, '')

    // Read markdown file as string
    const fullPath = path.join(postsDirectory, fileName)
    const fileContents = fs.readFileSync(fullPath, 'utf8')

    // Use gray-matter to parse the post metadata section
    const matterResult = matter(fileContents)

    // Combine the data with the id
    return {
      id,
      ...(matterResult.data as { date: string; title: string })
    }
  })
  // Sort posts by date
  if (count) {
    return allPostsData.sort((a, b) => {
      if (a.date < b.date) {
        return 1
      } else {
        return -1
      }
    }).slice(0, count)
  } else {
    return allPostsData.sort((a, b) => {
      if (a.date < b.date) {
        return 1
      } else {
        return -1
      }
    })
  }
}

export function getAllPostIds() {
  const fileNames = fs.readdirSync(postsDirectory)
  return fileNames.map(fileName => {
    return {
      params: {
        id: fileName.replace(/\.md$/, '')
      }
    }
  })
}

export async function getPostData(id: string) {
  const fullPath = path.join(postsDirectory, `${id}.md`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')

  // Use gray-matter to parse the post metadata section
  const matterResult = matter(fileContents)

  // Use remark to convert markdown into HTML string
  const processedContent = await remark()
    .use(html)
    .process(matterResult.content)
  const contentHtml = processedContent.toString()

  return {
    id,
    contentHtml,
    ...(matterResult.data as { date: string; title: string; imageUrl: string })
  }
}

Library di atas mempunya beberapa fungsi

  • getSortedPostsData: untuk mendapatkan daftar data post setelah diurutkan berdasarkan 'date'.
  • getAllPostIds: untuk mendapatkan semua id (nama semua file markdown tanpa extensi '.md')
  • getPostData: untuk mendapatkan data post (termasuk string HTML) berdasarkan id post

File 'pages/index.tsx'

Ubahlah file 'pages/index.tsx' dengan kode berikut:

import { getSortedPostsData } from "../lib/posts";
import Link from "next/link";
import { GetStaticProps } from "next";
import Image from "next/image";

export default function Home({
  allPostsData,
}: {
  allPostsData: {
    date: string;
    title: string;
    id: string;
  }[];
}) {
  return (
    <main>
      <div className="text-3xl text-center my-12 font-bold text-gray-600">
        <h1>My Blog</h1>
      </div>
      <div className="w-4/5 mx-auto">
        <h3 className="text-xl text-justify text-indigo-600 font-semibold">Daftar Artikel:</h3>
        <ul>
          {allPostsData.map(({ id, date, title }) => (
            <li key={id}>
              <Link href="/posts/[id]" as={`/posts/${id}`}>
                <a className="text-blue-500">{title}</a>
              </Link>
              <br />
              <small className="text-sm text-gray-400">{date}</small>
            </li>
          ))}
        </ul>
      </div>
    </main>
  );
}

export const getStaticProps: GetStaticProps = async () => {
  const allPostsData = getSortedPostsData();
  return {
    props: {
      allPostsData,
    },
  };
};

Perhatikan fungsi:

  • getStaticProps: menjalankan static generation untuk mendapatkan daftar data post
  • Home: melakukan render halaman home

Untuk melihat hasilnya jalankan perintah

yarn dev

Kemudian, buka browser anda dan akses 'localhost:3000/'

tampilan localhost:3000

Membuat Laman Post

Kita telah berhasil menampilkan daftar file markdown, selanjutnya kita memerlukan laman post untuk menampilkan data post berdasarkan id. Disini, kita memerlukan rute dinamis (dynamic routes) dimana URL yang akan digunakan adalah http://localhost:3000/posts/[id] dimana id diambil dari nama file markdown (contoh http://localhost:3000/posts/contoh-1).

Buatlah direktori 'pages/posts' dan buatlah file 'pages/posts/[id].tsx' (termasuk tanda kurung siku [], sebagai nama file, nextjs akan membaca ini sebagai rute dinamis) dan tempelkan baris kode di bawah ini!

import { getAllPostIds, getPostData } from "../../lib/posts";
import { GetStaticProps, GetStaticPaths } from "next";
import Link from "next/link";

export default function Post({
  postData,
}: {
  postData: {
    title: string;
    date: string;
    contentHtml: string;
  };
}) {
  return (
    <main>
      <div className="text-3xl text-center my-12 font-bold text-gray-600">
        <h1>My Blog</h1>
      </div>
      <div className="w-4/5 mx-auto">
        <article>
          <h1>{postData.title}</h1>
          <div className="text-gray-600">Tanggal: {postData.date}</div>
          <div
            className="pt-4 text-justify"
            dangerouslySetInnerHTML={{ __html: postData.contentHtml }}
          />
          <div className="my-4">
            <Link href="/">
              <a className="text-xl font-bold text-gray-600">ā† back to home</a>
            </Link>
          </div>
        </article>
      </div>
    </main>
  );
}

export const getStaticPaths: GetStaticPaths = async () => {
  const paths = getAllPostIds();
  return {
    paths,
    fallback: false,
  };
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const postData = await getPostData(params?.id as string);
  return {
    props: {
      postData,
    },
  };
};

Perhatikan fungsi di atas:

  • getStaticPaths: fungsi ini digunakan untuk generate path untuk semua id (file markdown)
  • getStaticProps: digunakan untuk mendapatkan data post berdasarkan id yang ada pada URL
  • Post: melakukan render data pada laman post

tampilan post

Penutup

Sampai disini kita telah berhasil membuat fungsi-fungsi dasar dari sebuah blog. Anda dapat melakukan modifikasi dengan menggunakan tailwindcss untuk mendapatkan tampilan yang Anda inginkan.

Adapun langkah berikutnya adalah melakukan deploy blog kita yang akan dibahas pada artikel berikutnya.

Semoga artikel ini bermanfaat bagi teman-teman yang sedang belajar nextjs.

Link Terkait

ā† Back to home