The blog

Writing from the workshop

202 articles on the parts that actually hurt.

section
Stop Guessing When Scrolling Stops — Use scrollend
JavaScript

Stop Guessing When Scrolling Stops — Use scrollend

For years, 'has the user stopped scrolling?' was a guess: debounce the scroll event, pick a delay, hope. The scrollend event answers the question directly — and as of Safari 26.2, it finally works everywhere. Here's the bug the guess causes, and the one-line fix.

Sep 29, 2026 · 5 min
Next.js Route Handlers: GET Stopped Caching in 15 — How to Cache in 16
Nextjs

Next.js Route Handlers: GET Stopped Caching in 15 — How to Cache in 16

Next.js 15 made GET Route Handlers dynamic by default. What that changes for code written for 14, how to cache one on purpose in 16, plus a cheat sheet.

Sep 29, 2026 · 15 min
getCurrentPosition() Doesn't Just Check — It Prompts
JavaScript

getCurrentPosition() Doesn't Just Check — It Prompts

You want to peek at whether location access is already granted before showing your own 'why we need this' banner. Calling getCurrentPosition() to find out fires the real system prompt. navigator.permissions.query() reads the state without ever asking.

Sep 26, 2026 · 5 min
NestJS Guards: CanActivate, ExecutionContext & Reflector
Nestjs

NestJS Guards: CanActivate, ExecutionContext & Reflector

A complete guide to NestJS guards: how CanActivate, ExecutionContext, and the Reflector decide who reaches your route handler, with a cheat sheet.

Sep 26, 2026 · 14 min
localStorage Isn't Free — It's Blocking Your Main Thread
JavaScript

localStorage Isn't Free — It's Blocking Your Main Thread

Autosave into localStorage looks harmless right up until the draft gets big. The API is synchronous by spec, so every write freezes the page for exactly as long as it takes — and debouncing doesn't fix it. IndexedDB does.

Sep 26, 2026 · 5 min
JSON.stringify Is Quietly Deleting Your File Uploads
JavaScript

JSON.stringify Is Quietly Deleting Your File Uploads

An avatar upload form that passes every test, ships to production, returns 200 OK — and never actually uploads the picture. The bug is one function call that silently turns a File into an empty object, with no error anywhere.

Sep 26, 2026 · 6 min
The Background Task That Waited 40 Seconds for 'Idle'
JavaScript

The Background Task That Waited 40 Seconds for 'Idle'

requestIdleCallback promises to run only when the browser has spare cycles — but on a page that's never truly idle, that promise has no upper bound. Here's why the callback can stall, why the timeout escape hatch has its own trap, and when to reach for it instead of setTimeout or scheduler.yield.

Sep 26, 2026 · 6 min
Vue Composables: The Shared State Trap (+ Cheat Sheet)
Vue

Vue Composables: The Shared State Trap (+ Cheat Sheet)

Why two Vue components using the same composable can end up sharing state by accident, the mental model that explains it, and how to fix or embrace it.

Sep 22, 2026 · 13 min
The Dropdown You've Been Faking for a Decade
CSS

The Dropdown You've Been Faking for a Decade

Every hand-built 'custom select' with icons and a styled popup exists because the real <select> couldn't do that. appearance: base-select just closed the gap — without you losing the native picker's keyboard nav and mobile UI.

Sep 22, 2026 · 5 min
Next.js proxy.ts Explained (with Cheat Sheet)
Nextjs

Next.js proxy.ts Explained (with Cheat Sheet)

Next.js 16 renamed middleware.ts to proxy.ts and locked it to the Node.js runtime. Learn the network-boundary model, matcher config, and the migration path.

Sep 22, 2026 · 12 min
Stop Writing JavaScript To Fix `100vh` On Mobile
CSS

Stop Writing JavaScript To Fix 100vh On Mobile

Your full-screen hero looks perfect in the simulator and cuts off the CTA on a real phone. It's not a bug in your code — it's `vh` answering a question you didn't ask. Three new units fix it with zero JavaScript.

Sep 22, 2026 · 6 min
React Derived State: Why That useState Is Probably a Bug
React

React Derived State: Why That useState Is Probably a Bug

React derived state explained: why syncing a computed value into useState with an Effect causes extra renders and drift, and what to write instead.

Sep 20, 2026 · 14 min