A markdown-first personal blog built with Next.js (Pages Router) and Tailwind CSS. Content lives in the repo, so you can ship without a CMS.
- Next.js 15 Pages Router with static posts/pages
- Server-side pagination at
/page/[page] - Tag pages at
/tags/[tag] - Auto table of contents for
h2toh4headings - Post stats (read time, word count, headings, code blocks, images)
- Prism-powered code highlighting + copy-to-clipboard
- Markdown extras: GFM, math (KaTeX), emoji shortcodes
- Content asset pipeline for
content/imagesandcontent/assets - Light/dark theme with persisted preference
- Optional open-source status widget (GitHub API)
- Node.js 22.x (per
package.jsonengines) - npm
git clone https://github.com/sondt99/Tech-Blog.git
cd Tech-Blog
npm install
npm run devOpen http://localhost:3000.
content/
images/
assets/
pages/
public/
src/
components/
layouts/
lib/
pages/
styles/
Note: content/ is not recursive. Only content/*.md are treated as posts.
- Posts:
content/*.md->/posts/<slug> - Pages:
content/pages/*.md->/<slug> - Tags: frontmatter
tags->/tags/<tag> - Assets:
content/images/*,content/assets/*->/api/content/<path>
Slug comes from the filename. Example: content/hello-world.md -> /posts/hello-world.
---
title: "Your Post Title"
date: "YYYY-MM-DD"
excerpt: "Short description for the post"
featured: "/images/featured.jpg"
tags:
- security
- systems
---Notes:
datecontrols sorting (newest first).featuredis optional and powers the hero image.tagscan be an array or a comma-separated string.
---
title: "About"
lastUpdated: "YYYY-MM-DD"
------
title: "About"
lastUpdated: "YYYY-MM-DD"
timeline:
- year: "2024"
category: "Work"
place: "Company Name"
role: "Security Engineer"
detail: "Team focus and highlights."
- year: "2022"
category: "Study"
place: "University Name"
role: "B.Sc. in Information Security"
---Place images in content/images/ and other files in content/assets/.
You can reference them in Markdown or frontmatter using any of:
/images/...orimages/.../assets/...orassets/...
The pipeline rewrites those to /api/content/... and serves them from disk.
Markdown files are blocked by the API route.
Markdown is processed in:
src/pages/posts/[slug].tsxsrc/pages/[slug].tsx
Enabled features:
remark-gfm(tables, task lists, strikethrough, footnotes)remark-math+rehype-katex(math)remark-emoji- Prism-based syntax highlighting
- TOC generated from
h2-h4
Raw HTML in Markdown is not rendered. If you need it, add rehype-raw and enable allowDangerousHtml in the pipeline (and keep sanitization in mind).
The home page can show an open-source status card. Configure it in site.config.ts under openSource.
- The API route is
src/pages/api/open-source-status.ts. - Set
GITHUB_TOKEN(orGITHUB_API_TOKEN,GH_TOKEN) to avoid GitHub rate limits. - If you want commit comparison, set
VERCEL_GIT_COMMIT_SHAorGIT_COMMIT_SHAin the environment.
- Site metadata, nav, labels:
site.config.ts - Home hero + pagination size:
src/pages/index.tsx(POSTS_PER_PAGE) - Post layout, stats, TOC:
src/pages/posts/[slug].tsx - Page layout + timeline:
src/pages/[slug].tsx - Theme tokens + typography:
src/styles/globals.cssandtailwind.config.ts
npm run dev: start dev servernpm run build: build for productionnpm start: run production servernpm run lint: lintnpm run clean: remove.nextandnode_modules
/page/[page] uses getServerSideProps, so you need a Node runtime (no next export).
Standard Next.js deployments work: npm run build then npm start, or deploy on Vercel with SSR enabled.
- Post not showing: ensure the file is in
content/(not a subfolder) and ends with.md. - Wrong order: check the
datefrontmatter value. - Images not loading: use
/images/...or/assets/...and put files incontent/imagesorcontent/assets. - Tags not linking: ensure
tagsis a list or comma-separated string.
- Thai Son Dinh (@sondt)