Just another blog about Sitecore, tips and tricks
A modern, high-performance blog built with Next.js 16, React 19, TypeScript, and Tailwind CSS. This blog focuses on Sitecore architecture, development tips, Azure, and web technologies.
- Modern Tech Stack: Built with Next.js 16 (App Router), React 19, and TypeScript 6.0
- Markdown-Based Content: Write blog posts in Markdown with frontmatter metadata
- Theme Support: Light, dark, and christmas theme switching with system preference detection
- Optimized Images: Next.js Image optimization with AVIF and WebP support
- Responsive Design: Mobile-first design with Tailwind CSS and responsive typography
- Reading Progress Bar: Visual indicator of reading progress on blog posts
- Infinite Scroll: Smooth infinite scrolling for browsing posts
- Auto-Scroll Navigation: Automatic scroll to next post functionality
- Tag & Category System: Organize and filter posts by tags and categories
- Archive View: Browse posts chronologically
- SEO Optimized: Built-in metadata, Open Graph, and Twitter Card support
- Google Analytics: Integrated analytics tracking
- Comprehensive Testing: E2E testing with Playwright and anti-flakiness patterns
- Node.js: >= 24.0.0
- pnpm: 12.4.1 (installed through Corepack)
- Clone the repository:
git clone https://github.com/avwolferen/blog.git
cd blog- Enable Corepack, which manages the pnpm version used by this project:
corepack enable
corepack install- Verify that pnpm is available:
pnpm --version- Install dependencies:
pnpm installThis repository is configured for GitHub Codespaces, providing a complete development environment in the cloud.
- Click the "Open in GitHub Codespaces" badge above or create a new codespace from the repository
- Wait for the environment to build (first time setup takes ~2-3 minutes)
- Once ready, the dependencies are automatically installed
- Start developing immediately with
pnpm dev
- Node.js 24 with TypeScript support
- Pre-installed VS Code extensions for Next.js, React, Tailwind CSS, and Playwright
- Playwright browsers automatically installed and configured
- Port forwarding for the Next.js dev server (port 3000)
- GitHub CLI for repository management
- Docker support for containerized workflows
All configurations are in .devcontainer/ directory.
Start the development server with Turbopack (faster):
pnpm devOr use Webpack:
pnpm dev:webpackOpen http://localhost:3000 to view the blog.
pnpm build
pnpm startpnpm type-checkpnpm lintpnpm check-imagesChecks every blog post for broken local image references and disallowed external image URLs.
The application sets strict response headers in next.config.js for all routes:
Content-Security-PolicyX-Frame-Options: DENYReferrer-Policy: strict-origin-when-cross-originPermissions-Policywith powerful features disabled (e.g.camera=(),microphone=(),geolocation=(),payment=(),publickey-credentials-get=()).
For this static blog, these features are not required, so disabling them reduces attack surface and limits unnecessary browser capability exposure. If future functionality needs one of these APIs (e.g. embedded capture tools or passkeys), explicitly update the header and tests together.
This project uses Playwright for end-to-end testing with comprehensive anti-flakiness patterns.
pnpm testpnpm test:uipnpm test:chromium
pnpm test:firefox
pnpm test:webkitpnpm test:mobilepnpm test:stable- Test Summary - Overview of all tests
- Anti-Flakiness Configuration - Detailed Playwright setup
- Anti-Flakiness Quick Reference - Best practices guide
- Quick Start Testing Guide - Getting started with tests
blog/
βββ .devcontainer/ # GitHub Codespaces configuration
βββ .github/ # GitHub Actions and workflows
βββ app/ # Next.js App Router pages
β βββ page.tsx # Homepage with featured posts
β βββ layout.tsx # Root layout with metadata
β βββ globals.css # Global styles
β βββ api/ # API routes (content endpoint)
β βββ archive/ # Archive page for all posts
β βββ blog/[slug]/ # Dynamic blog post pages
β βββ content/[...path]/ # Dynamic content pages
β βββ tags/ # Tag listing and filtering
β βββ not-found.tsx # 404 page
βββ components/ # React components
β βββ Header.tsx # Site header with navigation
β βββ Footer.tsx # Site footer
β βββ ThemeProvider.tsx # Light/dark/christmas theme provider
β βββ GoogleAnalytics.tsx # Analytics component
β βββ ReadingProgressBar.tsx # Reading progress indicator
β βββ InfiniteScrollBlog.tsx # Infinite scroll component
β βββ AutoScrollNext.tsx # Auto-scroll navigation
βββ content/ # Blog content (Markdown files)
β βββ blog/ # Individual blog posts
β β βββ [post-slug]/
β β βββ index.md # Post content with frontmatter
β β βββ *.png/jpg # Post images
β βββ assets/ # Shared assets
βββ lib/ # Utility functions
β βββ markdown.ts # Markdown processing utilities
βββ types/ # TypeScript type definitions
β βββ blog.ts # Blog post types
βββ tests/ # Playwright E2E tests
β βββ e2e/ # End-to-end test files
β βββ helpers/ # Test helper utilities
β βββ *.md # Testing documentation
βββ public/ # Static assets
β βββ robots.txt # SEO robots file
βββ next.config.js # Next.js configuration
βββ tailwind.config.ts # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
βββ playwright.config.ts # Playwright test configuration
βββ eslint.config.mjs # ESLint configuration (flat config)
- Create a new folder in
content/blog/with your post slug:
mkdir content/blog/my-new-post- Create an
index.mdfile with frontmatter:
---
title: "My New Post Title"
date: "2026-01-19"
categories: ["Sitecore", "Development"]
tags: ["next.js", "typescript", "tutorial"]
img: ./cover.jpg
---
Your post content goes here...- Add images to the same folder and reference them in your markdown:
- title (required): Post title
- date (required): Publication date in YYYY-MM-DD format
- categories (required): Array of category names
- tags (optional): Array of tag names for filtering
- img (optional): Path to cover image (relative to post folder)
Edit tailwind.config.ts to customize the color scheme. The blog includes light, dark, and christmas themes with custom color definitions.
Update metadata in app/layout.tsx:
export const metadata: Metadata = {
title: 'Your Blog Title',
description: 'Your description',
// ...
}Configure Google Analytics by setting the tracking ID in the GoogleAnalytics component in components/GoogleAnalytics.tsx.
Analytics loading is gated by explicit user consent via components/AnalyticsConsent.tsx.
script-src currently keeps 'unsafe-inline'.
We tested removing 'unsafe-inline' and observed breakage:
- browser CSP errors for blocked inline scripts (
Executing inline script violates ... script-src) - runtime/hydration failures (for example:
Expected a request ID to be defined for the document via self.__next_r) - consent banner interaction tests failing as a side effect of blocked runtime scripts
For now, 'unsafe-inline' is retained for compatibility with Next.js App Router runtime scripts. A stronger follow-up mitigation is to migrate to a nonce-based CSP (and pass nonces to all framework and analytics scripts) so script-src can be tightened without breaking functionality.
- Next.js 16 - React framework with App Router
- React 19 - UI library
- TypeScript 6.0 - Type safety
- Tailwind CSS 3.4 - Utility-first CSS framework
- @tailwindcss/typography - Beautiful typographic defaults for prose content
- Heroicons - Beautiful hand-crafted SVG icons
- gray-matter - Parse frontmatter from Markdown files
- marked - Fast Markdown parser and compiler
- Prism.js - Lightweight syntax highlighting
- Playwright - End-to-end testing framework
- @axe-core/playwright - Accessibility testing
- ESLint - JavaScript/TypeScript linting (flat config)
- TypeScript - Static type checking
- PostCSS - CSS transformation
- Autoprefixer - CSS vendor prefixing
Alex van Wolferen
- Sitecore MVP Technology 2018, 2021, and 2022
- Twitter: @avwolferen
- Website: alexvanwolferen.nl
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions, issues, and feature requests are welcome! Feel free to check the issues page.
Note: This blog focuses on Sitecore architecture, Azure development, and modern web technologies. Content reflects personal experiences and technical insights from a Sitecore MVP.