- Framework: Next.js 14 (App Router), TypeScript
- Content: Contentlayer2 — MDX files in
data/blog/anddata/authors/ - Styles: Tailwind CSS 3 (class-based dark mode, Space Grotesk font)
- Deployment: Vercel
- Package manager:
npm - Comments: Giscus (GitHub Discussions), configured via env vars
- Search: kbar (local
public/search.json, regenerated on build) - Analytics: Google Analytics via Pliny
npm run dev # start dev server
npm run build # production build + postbuild (RSS, search index)
npm run lint # ESLint with auto-fix| Alias | Resolves to |
|---|---|
@/components/* |
components/* |
@/data/* |
data/* |
@/layouts/* |
layouts/* |
@/css/* |
css/* |
contentlayer/generated |
.contentlayer/generated |
| Path | Purpose |
|---|---|
app/ |
Next.js App Router pages |
components/ |
Shared React components |
layouts/ |
Blog post layout templates |
data/blog/ |
MDX blog posts (41 posts) |
data/authors/ |
Author MDX profiles |
data/siteMetadata.js |
Site-wide config (title, URL, socials) |
public/static/images/ |
Static image assets |
scripts/ |
postbuild.mjs (RSS + search index) |
The following are already implemented — do not duplicate or replace them:
- Canonical URLs — set in
generateMetadata()inapp/blog/[...slug]/page.tsxviapost.canonicalUrl ?? computed URL BlogPostingJSON-LD — generated by Contentlayer incontentlayer.config.ts(includespublisher,mainEntityOfPage,author). Injected inapp/blog/[...slug]/page.tsxBreadcrumbListJSON-LD — injected alongside BlogPosting inapp/blog/[...slug]/page.tsxWebSiteJSON-LD — injected inapp/layout.tsx(covers all pages)- Publisher logo — uses
DaveGoosem.com_Logo_Black.png(not the white variant) so it is legible on white backgrounds as Google requires - Sitemap —
app/sitemap.ts, auto-generated, excludes drafts - Robots —
app/robots.ts, auto-generated
- Do not add API routes — this is a static/SSG blog with no backend
- Do not add a database or server-side state
- Contentlayer2 auto-generates TypeScript types on
npm run build/npm run dev— do not edit.contentlayer/manually - ESLint uses flat config (
eslint.config.mjs) — not.eslintrc - External links require
target="_blank"andrel="noopener noreferrer"(enforced by ESLint)
Contentlayer scans all of data/ — any plain .md file placed inside data/ (e.g. a CLAUDE.md) must be added to contentDirExclude in contentlayer.config.ts → makeSource(), otherwise Contentlayer warns and skips it at startup.
Content Security Policy — next.config.js contains a hand-written CSP string. If you add any external script, font, frame, or image source (e.g. a new analytics provider, embed, or CDN), add its hostname to the appropriate CSP directive in that file or the browser will silently block it.
Remote images — next/image only proxies domains listed in next.config.js → images.remotePatterns. Currently only picsum.photos is allowed. Add new domains there before using external image URLs in posts or components.
Bundle analysis — run ANALYZE=true npm run build to open the webpack bundle visualiser. Useful before committing large new dependencies.