This is a Next.js project bootstrapped with create-next-app.
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun devOpen http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying app/page.js. The page auto-updates as you edit the file.
This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.
The sitemap (/sitemap.xml) is generated by src/app/sitemap.js. It includes:
- Hand-listed static routes (
/,/talks,/projects,/blog, …) - Dynamic talk + project pages from
src/data/{talks,projects}.js - Cloudflare Bulk Redirect sources (optional, see below)
The route is regenerated hourly via export const revalidate = 3600, so
items added in Cloudflare appear within ~1h without a redeploy.
Cloudflare doesn't push events when you add a Bulk Redirect, so the sitemap pulls from the CF API on regeneration. Set these env vars in Vercel (Project → Settings → Environment Variables) to enable:
| Var | Required | Notes |
|---|---|---|
CLOUDFLARE_API_TOKEN |
yes | Scoped token with Account → Account Filter Lists: Read (read-only is enough at runtime) |
CLOUDFLARE_ACCOUNT_ID |
yes | Found in Cloudflare dashboard, right sidebar |
CLOUDFLARE_SITEMAP_HOSTS |
no | Comma-separated host whitelist (e.g. stimmie.dev,workshops.stimmie.dev,talks.stimmie.dev,links.stimmie.dev). Defaults to the value of CLOUDFLARE_SITEMAP_HOST or stimmie.dev |
CLOUDFLARE_SITEMAP_HOST |
no | Legacy single-host fallback (still honoured) |
CLOUDFLARE_REVALIDATE |
no | TTL in seconds for CF API fetch cache. Defaults to 3600 |
Without these vars the integration is a no-op; the sitemap still builds
fine. Transient API errors are logged to console.warn and do not break
the build.
Wildcard sources (* in the redirect source URL) and asset paths
(.png, .css, etc.) are filtered out so the sitemap only contains
crawlable HTML pages.
The default behaviour is to consult getSitemapHosts() from
src/data/redirects.js so apex + every category subdomain (workshops,
talks, links) are covered without extra config.
The single source of truth for short-link redirects lives in
src/data/redirects.js and is consumed by:
middleware.js: handles<category>.stimmie.dev/<slug>at the edge runtimesrc/app/r/[...slug]/page.js: handlesstimmie.dev/r/<slug>on apexscripts/cf-migrate-redirects.mjs: syncs the map into a Cloudflare Bulk Redirect List so the redirect happens at Cloudflare's edge, before the request ever reaches Next.js.
To run the migration (idempotent, safe to re-run):
# Token needs:
# Account → Account Filter Lists: Edit
# Account → Account Rulesets: Edit
export CLOUDFLARE_API_TOKEN=...
export CLOUDFLARE_ACCOUNT_ID=...
# Plan only; no mutations:
npm run cf:migrate-redirects -- --dry-run
# Apply changes:
npm run cf:migrate-redirects
# Apply + delete CF items missing from the local map:
npm run cf:migrate-redirects -- --pruneThe script:
- Creates (or finds) a Bulk Redirect List named
stimmie_redirects. - Computes a diff between local + CF state, then adds/deletes items.
- Ensures the account-level
http_request_redirectruleset has a rule that consults the list (http.request.full_uri in $stimmie_redirects).
After the migration runs, both the apex /r/<slug> and the
<category>.stimmie.dev/<slug> URLs redirect at the CF edge. The
in-app middleware + /r/ page remain as graceful fallbacks for local
dev and preview deployments.
This repo uses semantic-release
to automate versioning, CHANGELOG.md, and GitHub Releases from
Conventional Commits.
The commit type controls what kind of release (if any) is produced:
| Type | Bump | Appears in changelog |
|---|---|---|
feat: |
minor | yes (Features) |
fix: |
patch | yes (Bug Fixes) |
perf: |
patch | yes (Performance) |
refactor: |
patch | yes (Refactors) |
docs: |
patch | yes (Documentation) |
BREAKING CHANGE:* |
major | yes |
chore: ci: style: test: build: |
none | no |
*Either include BREAKING CHANGE: in the commit body, or use feat!: / fix!: etc.
- A commit lands on
main. .github/workflows/release.ymlrunsnpx semantic-release.- semantic-release inspects all commits since the last tag, decides the next
version, updates
package.json+CHANGELOG.md, tags the commit, and creates a GitHub Release. - The bot's release commit is marked
[skip ci]so it does not retrigger.
npx semantic-release --dry-run --no-ciThis prints what would be released without actually creating tags or pushes.
If this project helped you out, consider treating me to a coffee ☕
- Tech Stack: React, TailwindCSS, Next.js, Node.js/NPM
- Repository Size: 513 tracked files
- Latest Update:
b5f9aac chore: add stale issue and PR validators
☕ If you found this project useful, you can support my work at kape.stimmie.dev!