Technical Analysis of This Website

July 1, 2026

This article covers the technical structure of this personal blog—from the Next.js frontend, content system, Route Handlers, and external data integration to building and hosting.

Most of this article was generated by AI and reviewed and edited by me.

In one sentence:

This is a personal blog built with Next.js App Router: articles are managed as MDX files, pages are rendered by React components, a small amount of dynamic data comes from Notion and unofficial APIs, it's deployed on Vercel, and Cache Components are used for caching the critical data layer.

Frontend

The frontend is primarily built with Next.js 16 + React 19 + TypeScript, utilizing the App Router and file-based routing. Core routes include:

  • /: Homepage, featuring an introduction, recommended page cards, Micro Logs, and an article list.
  • /about: Personal profile.
  • /projects: Project index.
  • /{year}/{slug}: Formal blog posts, e.g., /2026/AI-B2BB-CRM.
  • /psychology-effects: Psychology effects collection (Notion data source).
  • /micro-logs: Lightweight log stream (Notion data source).
  • /org-chart-web: Organizational chart web editing tool.
  • /links/[id]: Smart redirect links with Open Graph metadata.
  • /atom: Atom Feed.

The UI layer is mainly composed of Tailwind CSS 3, custom React components, and MDX content components. Interactive or external embed modules like Mermaid, Tweet, Bilibili, and YouTube are loaded on-demand; Mermaid remains a Client Component, while Tweet / Bilibili are rendered after fetching data on the server.

Layouts are divided into three route groups by purpose:

Route GroupPath ExamplePurpose
(post)
/(post)/2026/about-this-site
Blog posts, with TOC and article navigation
(page)
/(page)/psychology-effects
Topic page / Tool page
(no-layout)
/(no-layout)/org-chart-web-editor
Full-screen editor without site main layout

This frontend is not a landing page structure, but more like a long-term reading interface: the homepage serves as the entry point and index, article pages are for in-depth reading, and topic pages are for displaying Notion-synced data or interactive tools.

Content System

The content layer is not a headless CMS, but file-based MDX + JSON indexing.

Current main types:

TypeSourceIndexPurpose
posts
app/(post)/{year}/{slug}/page.mdx
app/index-posts.json
Official blog posts
featured pages
app/(page)/*/page.mdx
app/index-featured.json
Featured cards on homepage
static pages
app/about/page.mdx, app/projects/page.mdx
Top-level informational pages
notion pages
Notion database
Hardcoded pageId in code
Micro Logs, Psychological Effects

Each article is an independent directory containing page.mdx, optionally components.tsx or notion.ts. This means each individual article can load arbitrary modules, have custom layouts, and leverage Next.js automatic code splitting—redundancy in a single article does not slow down the entire site.

The index file maintains metadata (id, date, title, url, order) for articles and featured pages. Pages read from the index via getPosts() and getFeaturedPages(), rather than scanning the filesystem at build time.

New articles can be created using the pnpm new-post script: interactively input a title and description, automatically generate an MDX skeleton, and write the entry to the index (Chinese titles can be converted to English slugs via the Tencent Cloud Translation API).

Markdown Pipeline

The Markdown build pipeline is based on @next/mdx, configured in next.config.js:

  • pageExtensions includes md and mdx, allowing MDX files to serve directly as pages.
  • experimental.mdxRs: true: Enables the Rust-based MDX compiler for faster compilation.
  • cacheComponents: true: Enables the Next.js 16 Cache Components model.

mdx-components.ts maps MDX elements to custom components, forming the site's style guide:

CategoryComponent
Typography
H1 / H2 / H3, P, A, OL / UL / LI, Blockquote, HR
Code
Code, Snippet (replaces <pre>)
Media
Image, Figure, Caption
Embeds
Tweet, YouTube, Bilibili, Mermaid
Auxiliary
Callout, DataTable, Footnotes (Ref / FootNotes / FootNote)
Chinese Fonts
Kai (KaiTi), Song (SongTi), Hei (HeiTi), JHSong (JingHua Old SongTi)

Heading anchors support the [#custom-id] notation: by adding a custom id at the end of a heading, H1/H2/H3 components will automatically generate a visible # link. The TOC component on the left side of the article page scans the headings within article on the client side and prioritizes reading these custom anchors.

Fonts and Theme

The font strategy balances Western and Chinese typography:

  • Western: Inter (body text), Kanit (Logo), Roboto Mono (monospace).
  • Chinese: Founder Kai/Song/Hei (cn-fontsource), Sarasa Gothic Mono SC (subsetted, retaining only commonly used Chinese characters), Jinghua Old Song (subsetted).
  • Subsetting scripts are located at scripts/subset-sarasa-font.js and scripts/subset-jhsong-font.js, with outputs in public/fonts/.

Theme switching is executed before the initial screen render via an inline themeEffect script, supporting light / dark / system states, and is persisted with localStorage. Vercel Analytics and Speed Insights are injected into the root layout.

Backend

Server-side capabilities are provided by Next.js Route Handlers, with code in app/api/ and several route.ts files:

EndpointPurpose
app/api/posts/route.ts
Returns post list JSON
app/api/featured/route.ts
Returns featured page list
app/api/posts/view/route.ts
Post view count (framework built, incr pending implementation)
app/api/pages/view/route.ts
Page view count
app/api/bilibili/route.ts
Bilibili video metadata
app/atom/route.ts
Generates Atom Feed
app/opengraph-image/route.tsx
Homepage OG image
app/(post)/og/[id]/route.tsx
Single post OG image
app/about/opengraph-image/route.tsx
About page OG image

Upstash Redis serves as a cache layer for external APIs: Tweet and Bilibili video information is fetched on the server side and written to Redis, falling back to the cache on request failures.

Unofficial Notion API (app/(page)/psychology-effects/notion.ts) is used to read two databases:

  • Micro Logs: Short logs displayed on the homepage and /micro-logs.
  • Psychology Effects: Experimental entries for /psychology-effects.

This data is retrieved via getMicroLogs() and getExps(), and cached using "use cache" + cacheTag.

External Data and Caching

Next.js 16 removed implicit fetch caching, and the project has migrated to the Cache Components model:

export async function getPosts() {
  "use cache";
  cacheLife("minutes");
  cacheTag("posts");
  // ...
}
Data FunctionsSourceCache Strategy
getPosts()
index-posts.json
"use cache" + cacheLife("minutes") + cacheTag("posts")
getFeaturedPages()
index-featured.json
"use cache" + cacheTag("featured-pages")
getMicroLogs()
Notion API
"use cache" + cacheTag("micro-logs")
getExps()
Notion API
"use cache" + cacheTag("psychology-effects")
Tweet / Bilibili
External API + Redis
Server-side fetch, Redis persistence

The client no longer uses SWR polling or setInterval to refresh Notion data; updates rely on cache expiration or subsequent webhook + revalidateTag for on-demand invalidation.

proxy.tsx (Next.js 16 replacement for middleware) currently only injects the x-edge-age response header for observing edge TTL.

Build & Run

Package management uses pnpm.

Common commands:

pnpm dev          # Development server (binds to 0.0.0.0)  
pnpm build        # Production build  
pnpm start        # Production start  
pnpm new-post     # Interactive post creation  
pnpm format       # Prettier formatting  
pnpm format:check # Format check

Build is done by next build, with Turbopack enabled by default. MDX pages are compiled into React components at build time; OG image routes are dynamically generated on request (ImageResponse + local woff fonts).

Environment variables (.env.example) are mainly for local scripts: Tencent Cloud Translation API key (pnpm new-post) and Upstash Redis Token (Tweet / Bilibili cache).

Hosting & Observability

The site is hosted on Vercel, domain jonaszhou.com.

  • Client analytics: @vercel/analytics
  • Performance monitoring: @vercel/speed-insights
  • Feed: /atom provides Atom XML
  • OG: Each page and article has a dynamically generated Open Graph image

Why This Design

This architecture suits my usage patterns:

  • MDX is suitable for long-term writing, and each post can be an independent "mini-app."
  • JSON indexing allows the homepage and feed to avoid filesystem scanning, keeping maintenance manageable.
  • Notion serves as the editing interface for Micro Logs and experimental data, which is faster to write than modifying code.
  • Next.js App Router keeps content, components, APIs, and OG generation in a single project.
  • Cache Components allow static shells to coexist with Notion dynamic data without requiring client-side polling.
  • Redis caches external API results to prevent Tweet / Bilibili embeds from slowing down pages or triggering rate limits.

It's not the simplest blog architecture—no full CMS using Notion or Contentlayer directly, and no multilingual routing—but it brings "personal profile, formal articles, lightweight logs, interactive tools, external embeds" into one maintainable Next.js project, while preserving room for further evolution with Cache Components and on-demand invalidation.