Personal Blog (Next.js + Contentlayer)
This is a personal blog built with Next.js 13 (App Router), Contentlayer, and Tailwind CSS.
Markdown content (posts & pages) lives in a separate repository and is consumed via a git submodule.
Tech Stack
- Framework: Next.js 13 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS + Typography plugin
- Content: Markdown via Contentlayer (
contentlayer/source-files) - Theming:
next-themes(light / dark mode) - Content source: Git submodule
content→personal-blog
Project Structure
app/– Next.js App Router pagesapp/page.tsx– Home pageapp/blog/– Blog index and individual post pagesapp/pages/– Static pages (e.g. 關於作者)
components/– Layout shell, header, footer, MDX/markdown components, theme togglelib/config.ts– Site configuration derived from environment variablesposts.ts– Helpers for querying posts & pages from Contentlayer
content/– Git submodule pointing topersonal-blog(markdown content)posts/– Blog posts (.md)pages/– Static pages (.md)assets/– Images referenced from markdown
public/assets– Symlink tocontent/assetsfor serving images at/assets/...contentlayer.config.ts– Contentlayer document type definitions and markdown pipeline
Prerequisites
- Node.js 18+ (recommended by Next.js 13)
- npm (comes with Node)
Setup
-
Clone the repository
git clone https://gitea.gbanyan.net/gbanyan/blog-nextjs.git cd blog-nextjs -
Initialize the content submodule
git submodule update --init --recursiveThis checks out the
contentsubmodule pointing topersonal-blog, which contains:posts/– posts in markdownpages/– static pages in markdownassets/– images used by posts/pages
-
Install dependencies
npm install -
Configure environment variables
Copy the example env file and update it with your personal information:
cp .env.local.example .env.localThen edit
.env.local:NEXT_PUBLIC_SITE_NAME="Your Name" NEXT_PUBLIC_SITE_TITLE="Your Personal Site" NEXT_PUBLIC_SITE_DESCRIPTION="Personal homepage and blog." NEXT_PUBLIC_SITE_URL="https://your-domain.example" NEXT_PUBLIC_SITE_AUTHOR="Your Name"These values are used by
lib/config.tsto customize:- Site title and description
- Default SEO metadata
- Header title
- Footer author name
-
Run the development server
npm run devThen open http://localhost:3000 in your browser.
Content Model
Contentlayer is configured in contentlayer.config.ts to read from the content submodule:
-
Posts
- Path:
content/posts/**/*.md - Type:
Post - Important frontmatter fields:
title(string, required)slug(string, optional – overrides path-based slug)tags(string list, optional)published_at(date, optional)description(string, optional – used as excerpt / meta)feature_image(string, optional – usually../assets/xxx.jpg)
- Path:
-
Pages
- Path:
content/pages/**/*.md - Type:
Page - Important frontmatter fields:
title(string, required)slug(string, optional)description(string, optional)feature_image(string, optional)
- Path:
Images
-
Markdown uses relative paths like:
 -
At build time, a small rehype plugin rewrites these to
/assets/my-image.jpg. -
public/assetsis a symlink tocontent/assets, so Next.js serves them as static files. -
feature_imagefields are also mapped from../assets/...→/assets/...and rendered above the article content.
Available npm Scripts
npm run dev– Start Next.js dev server with Contentlayer integration.npm run build– Runcontentlayer buildand thennext buildfor production.npm run start– Start the production server (afternpm run build).npm run lint– Run Next.js / ESLint linting.npm run contentlayer– Manually runcontentlayer build.
Deployment Notes
- This is a standard Next.js 13 App Router project and can be deployed to:
- Vercel
- Any Node.js host running
npm run build && npm run start
- Make sure to:
- Provide the same environment variables in your hosting environment as in
.env.local. - Initialize the
contentsubmodule in your deployment pipeline (or vendor the generated.contentlayeroutput if needed).
- Provide the same environment variables in your hosting environment as in
License
This is a personal project. No explicit open-source license is provided; all rights reserved unless otherwise noted.