The tech stack used on this site

You can get a pretty good sense of it just by looking at the package.json. So here it is:

package.json
{
    "dependencies": {
    "@radix-ui/react-avatar": "^1.1.1",
    "clsx": "^2.1.1",
    "gray-matter": "^4.0.3",
    "next": "15.0.0",
    "react": "19.0.0-rc-65a56d0e-20241020",
    "react-dom": "19.0.0-rc-65a56d0e-20241020",
    "zenn-content-css": "^0.1.157",
    "zenn-markdown-html": "^0.1.157"
    },
    "devDependencies": {
    "@biomejs/biome": "1.9.4",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
    }
}

Framework

I am using Next.js. While Astro is also a viable option for static generation, I chose Next.js for several reasons:

  • I have more opportunities to use Next.js in my work.
  • I find it easier to work with since I'm already familiar with it.
  • I genuinely enjoy using Next.js.

Formatter and Linter

I am using Biome for formatting and linting.

UI Components

I don't use any libraries for UI components, but I may occasionally rely on a headless component library like Radix UI.

Styling

I am using Tailwind CSS. Personally, I find it hard to go back to other styling methods. Even for small sites, using Tailwind CSS significantly changes the development experience (mainly because I'm used to it and enjoy working with it).

Content Storage

I manage markdown files in a separate repository. I retrieve files using the GitHub API.

For example, here's how it looks for a list:

posts/index.tsx

const res = await fetch(
  `https://api.github.com/repos/username/repository/contents/directoryName`,
  {
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
    },
  }
);

const resPosts = await res.json();
const mdFiles = resPosts.filter((article: any) => article.name.endsWith(".md"));

return (
  <ul>
    {mdFiles.map((file: any) => (
      <li key={file.name}>{file.name}</li>
    ))}
  </ul>
);

Content Detail Display

I retrieve the content of files using the GitHub API. Additionally, I embed frontmatter within the markdown, which I parse using gray-matter.

The frontmatter includes information such as the content's title and publication date, and I convert the markdown to HTML using zenn-markdown-html.

Here's how it looks:

posts/[id].tsx

const res = await fetch(
  `https://api.github.com/repos/username/repository/contents/directoryName/${id}.md`,
  {
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
    },
  }
);

const resPost = await res.json();

// The content is base64 encoded, so we decode it and convert it to utf-8
const content = Buffer.from(resPost.content, "base64").toString("utf-8");
const frontmatter = extractFrontmatter(content);

const post: {
  id: string;
  title: string;
  publishedAt: string;
  content: string;
} = {
  id: resPost.name.replace(".md", ""),
  title: frontmatter.data.title,
  publishedAt: frontmatter.data.publishedAt,
  content: frontmatter.content,
};

return (
  <div>
    <h1>{post.title}</h1>
    <div dangerouslySetInnerHTML={{ __html: markdownToHtml(post.content) }} />
  </div>
);

To apply the Zenn markdown style, I import the CSS in the layout.

By the way, Zenn is a well-known platform in Japan for posting technical articles, and I'm using its theme.

layout.tsx
import "./globals.css";
import "zenn-content-css"; // Apply Zenn markdown styles

...

Deployment

I use Vercel for deployment. It's very convenient.