Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

176 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sanderdesnaijer.com

Live Site Built with Next.js CMS Deployed on Vercel

Personal portfolio and blog of Sander de Snaijer, a frontend developer from the Netherlands with 10+ years of experience.

This site is where code meets creativity: webcam-controlled browser games built with MediaPipe, 3D printing experiments with resin and Arduino, Flutter apps on the App Store, and tutorials about all of it. Built as a fully SEO-optimized Next.js application with structured data, dynamic sitemaps, and tag-based content hubs.

Live at: www.sanderdesnaijer.com

What's on the site

  • Projects / face-controlled Tetris, webcam Duck Hunt with hand tracking, a 3D-printed Arduino word clock, a multi-language Sudoku app, geospatial weather tools, and more
  • Blog / step-by-step guides on 3D printing with resin, MediaPipe hand and face tracking in the browser, building with Next.js and Sanity, Flutter app development, and creative coding
  • Tags / 50+ topic pages that group projects and articles by technology (React, Flutter, MediaPipe, Arduino, 3D printing, etc.)

Tech stack

  • Next.js 16 / App Router, static generation with ISR, dynamic metadata
  • TypeScript / end-to-end type safety
  • Sanity / headless CMS with real-time previews and structured content
  • Tailwind CSS / utility-first styling
  • Playwright / end-to-end testing
  • Jest / unit testing
  • Vercel / deployment with preview branches

SEO features

  • JSON-LD structured data (BlogPosting, FAQPage, Person, WebSite, BreadcrumbList, SoftwareApplication)
  • Open Graph and Twitter Card meta tags with optimized images
  • Dynamic sitemap generation
  • Tag hub pages with unique descriptions and intro content
  • Content-first title strategy for better SERP click-through rates
  • FAQ schema on blog posts for rich result eligibility

Getting started

Copy .env.example to .env.local and fill in the required values:

cp .env.example .env.local

Then run the development server:

npm run dev

Open http://localhost:3000 to see the result.

Project structure

app/           Next.js App Router pages, components, and utilities
sanity/        Sanity schema definitions and studio config
e2e/           Playwright end-to-end tests, fixtures, and snapshot tooling
messages/      i18n translation files
public/        Static assets and favicons
redirects/     Blog redirect mappings (old Medium slugs)
scripts/       Build and migration scripts

Updating E2E fixtures

E2E tests run against local JSON fixtures instead of live Sanity, making them offline, deterministic, and free of API usage. After publishing new content in Sanity, update the fixtures:

npx tsx e2e/scripts/snapshot-fixtures.ts

This pulls current production data and writes it to e2e/fixtures/. Commit the updated fixtures so CI uses them too.

Environment variables

See .env.example for all required variables. You'll need credentials for Sanity, Vercel, and Google site verification.

Exporting blog posts to dev.to

Export a blog post from Sanity to dev.to-compatible markdown:

npm run export:devto -- <slug>

List all available blog post slugs:

npm run export:devto

The exported markdown file lands in scripts/devto-exports/<slug>.md with frontmatter, cover image, tags, and canonical URL pre-filled. Review the content and paste into dev.to/new. Set published: true when ready to go live.

RSS feed

The site exposes a full-content RSS 2.0 feed at /feed.xml (app/feed.xml/route.ts). Each item includes the complete article as HTML in <content:encoded>, rendered from the Sanity Portable Text body, plus title, link, canonical guid, publish date, author, tags, and the cover image as an enclosure. The feed is statically generated and refreshed on-demand: the Sanity webhook (/api/revalidate) revalidates /feed.xml when a blog post or tag changes, with a 24h time-based backstop. A discovery <link rel="alternate" type="application/rss+xml"> is advertised on all public pages.

Republishing blog posts to HackerNoon

HackerNoon imports from the RSS feed above (its Editor 2.0 has no reliable markdown paste import). Go to app.hackernoon.com/new, hover "Import Story", choose RSS Feed, and point it at https://www.sanderdesnaijer.com/feed.xml (or use Direct URL with a single post link). Set the canonical URL in Story Settings and submit for review.

IndexNow

The site supports IndexNow for instant URL submission to Bing, Yandex, and other participating search engines. The verification key is served from public/.

Submit all pages from the sitemap:

node scripts/indexnow.mjs

Submit specific URLs after publishing new content:

node scripts/indexnow.mjs https://sanderdesnaijer.com/blog/my-new-post

Run once per publish. Don't resubmit the same URLs repeatedly.

License

MIT

About

Frontend developer portfolio and blog built with Next.js, Sanity CMS, and TypeScript. Projects include webcam games with MediaPipe, 3D printing experiments, and Flutter apps.

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages