Skip to main content

Writing

Blog

Notes on commerce engineering, AI integrations, and storefront patterns from production work. Next.js, Centra, Algolia, Convex, and what actually ships.

All posts

When srcset maxes at 1000w on retina PDP

Sander Korf4 min read

PDP packshots looked soft on phones while cards stayed sharp. sizes asked for retina pixels but srcset stopped at 1000w. I matched candidates and one builder.

nextjssanityplaywright+1

Read more

Centra incomplete address is permanent

Sander Korf3 min read

A paid Centra order with a blank city blocked our warehouse export queue for eighty minutes. Classify bad address data as permanent and ack the event.

centratypescriptmonorepo

Read more

Superseded listen start must not call stop

Sander Korf4 min read

Guided session app granted mic access then showed could not start listening. A stale Listen start called stop and bumped the generation counter past the winner.

reactwebsocketsaudio

Read more

Tax estimate is not the creator payout

Sander Korf4 min read

Creator marketplace cards reused a Stripe Tax preview total as payout. Creators read fee plus tax as cash in hand. Split payoutCents from the estimate.

reacttypescriptpayments

Read more

Withhold ended LLM steps after move-on

Sander Korf3 min read

Fast models re-showed three resistance memories after move-on. Ended-step instructions must leave the system prompt, not fight a movedOn flag.

llmconvextypescript

Read more

Nested Embla: watchDrag and viewport gutters

Sander Korf4 min read

On a Centra PDP, nested Embla carousels for card media and edge-bleed rails hit two traps. Use watchDrag on the parent; safe-area inset on the viewport.

centraemblanextjs+1

Read more

Instant Nav needs an SSR shell, not ssr false

Sander Korf3 min read

Fashion liveshow soft nav painted an empty shell. Instant Nav locks static HTML; next/dynamic with ssr false and cookie awaits blocked landmarks e2e needed.

nextjsbambuserperformance

Read more

Revalidate type lists, not only references

Sander Korf3 min read

Liveshow detail refreshed after Sanity edits but the overview stayed stale for hours. Type-based GROQ lists never matched reference-only revalidation.

sanitynextjscache

Read more

Sparse webhooks need hydrate or a 503 retry

Sander Korf3 min read

BamHub liveshow webhooks sync into Sanity for a public listing. Sparse payloads left shows unpublished until I hydrated from the API or returned 503 for retry.

sanitynextjsbambuser

Read more

Late STT must no-op after transcript purge

Sander Korf4 min read

Purge deleted transcript overlays while vendor STT still ran. Completion handlers recreated erased text. Missing overlay row means return, not upsert.

convex

Read more

Check both rate limits before you spend

Sander Korf4 min read

STT under per-thread and global Convex buckets spent the thread token when global was empty. Check both before limit and throw so the mutation rolls back.

convex

Read more

Map null keys collapse Shopify live variants

Sander Korf3 min read

Shopify PLPs showed wrong Size facets when variant overrides keyed a Map by null shopifyProductId on consolidated locales. Use the store Product GID.

algoliashopifysanity

Read more

Algolia v5 replaceAllObjects false-timeouts

Sander Korf4 min read

Fashion full-catalog rebuilds false-fail when replaceAllObjects hits fifty waitForTask polls. Large indexes need a custom copy, batch, and move path.

algolianextjsshopify

Read more

RN Modal kills GestureDetector button taps

Sander Korf3 min read

Accept buttons in my Expo app worked everywhere except RN Modal dialogs. GestureDetector taps died silently because Modal renders outside the gesture root.

exporeact-nativegesture-handler

Read more

Keep nav backdrop open until close completes

Sander Korf4 min read

On a fashion storefront mega menu, the dim layer snapped off when Base UI cleared the menu value at the start of the exit animation, not the end.

nextjsreacta11y

Read more

Nested remote cache breaks Next.js prerender

Sander Korf4 min read

Homepage prerender on a Centra Next.js build failed with 'Filling a cache during prerender timed out'. Nested use cache remote boundaries caused the hang.

nextjscentraperformance

Read more

Next redirect 441 in a Cache Components admin

Sander Korf4 min read

Soft-nav to a Convex movement catalog hit React error 441 when a redirect streamed in Suspense. I render the index and pick the default group client-side.

nextjsreactconvex

Read more

Centra card video: muted autoplay after mount

Sander Korf3 min read

PLP cards with optional Sanity video stay silent or flash wrong when autoPlay lives in SSR. I start muted loops in a client effect and respect reduced motion.

centrasanitynextjs

Read more

Convex Identify reducer before admin UI

Sander Korf4 min read

Clinicians read Identify as a spreadsheet. Engineers cannot prove it ends. I seed Convex, write a pure reducer, and replay the sheet paths in Vitest first.

convexvitesttypescript

Read more

Pick a winner when Identify Compare stalls

Sander Korf4 min read

When two movements stay yes after Compare, looping the same chips teaches nothing. I pause for a pick, hatch the losers, and end the run as a hypothesis.

convextypescriptvitest

Read more

Lock Identify rules to a typed Convex menu

Sander Korf4 min read

Identify is a clinician movement engine on Convex. Spreadsheet authors invent words weekly; lock metrics and actions to validators so the reducer stays boring.

convextypescript

Read more

Klaviyo email video? Poster, not video tag

Sander Korf4 min read

Gmail and Outlook strip video in campaigns. Ship a poster image with an optional play overlay and an outbound link baked at send time from the CMS.

klaviyoreact-emailsanity

Read more

Biome bans vendor imports in shop UI

Sander Korf3 min read

On a Centra fashion storefront, product cards broke when commerce API types leaked into JSX. Map to storefront DTOs and ban vendor imports in shop components.

centrasanitybiome+1

Read more

Centra CanonicalPath fixes Instant Nav shell

Sander Korf3 min read

Collection pages stuck on loading after Instant Nav. Own PLP slugs per locale, 308 the rest, and skip waiting on empty query strings for the shell.

centranextjsseo

Read more

Server-side country labels fix E2E fills

Sander Korf3 min read

Checkout country dropdowns rebuilt after hydration and emptied a test fill. Precompute localized names on the server before the form paints.

intlnextjsplaywright

Read more

Centra PDP add to cart stays pressable

Sander Korf4 min read

A fashion PDP that greys out add to cart until size is picked looks broken. Validate on submit, show the hint after a miss, then focus the chips.

centranextjstailwind

Read more

Convex session audio purge is terminal-only

Sander Korf3 min read

A research product records session audio apart from chat. Deleting a thread must not wipe the rolls. Admin purge is terminal-only and deletes chunks first.

convexnextjs

Read more

Sanity Look pins on a Centra PDP cover crop

Sander Korf3 min read

Editors pin Shop the Look dots on the full photo. A tall cover crop on the product page slides those dots off the garment unless you remap them once.

sanitycentranextjs+1

Read more

Centra colour siblings dedupe by uri

Sander Korf4 min read

Colour swatches showed Black twice on a fashion PDP. Dedupe siblings on the URL you paint, then hide the picker when only one colour exists.

centranextjstailwind

Read more

Centra size guide transposes the chart

Sander Korf3 min read

Fashion size guides store measurements as rows. Shoppers need size rows on the product page. Flip the sheet once so the numbers match the layout.

centranextjstailwind+1

Read more

Centra OOS size swaps the primary CTA

Sander Korf4 min read

An out of stock size on a fashion product page should not grey out checkout. Flip the main button to a notify action and reuse one overlay for every sheet.

centranextjstailwind+1

Read more

CSS view() timelines vs IntersectionObserver

Sander Korf4 min read

Below-the-fold blocks on my Next.js site faded in with a client observer. Scroll timelines do the fade now, and older browsers just show the content.

cssnextjstailwind+1

Read more

Next.js MDX in HTML, not behind Suspense

Sander Korf4 min read

Crawlers never ran my blog JavaScript, so they got a shell with no article. I flattened the static pages and now test the HTML with scripts turned off.

nextjsmdxplaywright+1

Read more

Ship Convex with Vercel Git, not Actions

Sander Korf4 min read

Preview and production disagreed because one push deployed twice. I let Vercel Git own the build, run Convex there, and keep Actions for tests only.

vercelconvexgithub-actions+1

Read more

Algolia filter taps paid INP for new objects

Sander Korf4 min read

Fashion PLP filter chips felt frozen when an observability hook retried on new object refs each render. Key the effect on queryID, not the results bag.

algoliainstantsearchnextjs+2

Read more

Homepage skip links jumped the fold late

Sander Korf3 min read

I awaited a cached translation for a skip link and the Shopify homepage jumped. That one string now renders inside the section that already waits.

next-intlnextjssanity+1

Read more

Next.js Suspense fallbacks wreck CLS P95

Sander Korf3 min read

Empty loading slots made my CMS blocks jump in late and wrecked layout stability. I now reserve height per section type and watch P95, not P75.

nextjssanityshopify+2

Read more

Sanity labels hid my Algolia merch pins

Sander Korf3 min read

On a fashion storefront PLP, merchandisers pin hero SKUs in Sanity CMS but Algolia ignores them when Groq filters on the desk title instead of the schema id.

algoliasanitynextjs+1

Read more

Keep Algolia admin keys off the storefront

Sander Korf3 min read

A fashion PLP runs Algolia InstantSearch in the browser. Only the search-only key belongs in client code; writes stay on the server with the admin key.

algoliainstantsearchnextjs+1

Read more

Centra to Klaviyo tags need a plugin contract

Sander Korf4 min read

Fashion checkout mail never arrives if you invent the event names. Copy the plugin tags and split the catalog id so receipts and restock emails actually send.

centraklaviyo

Read more

Dutch Klaviyo receipts arrived in English

Sander Korf3 min read

Dutch shoppers got English order emails from Klaviyo. I now pick the storefront language on the server before the event leaves, not in a default template.

next-intlklaviyocentra+1

Read more

Staging deep links must not hijack production

Sander Korf3 min read

A mobile app ships production and staging builds. Reusing production Universal Link hosts sends QA to the store app and makes staging reset links look broken.

expoeasdeeplinks

Read more

EAS SHA-1 fails under Worklets Bundle Mode

Sander Korf4 min read

My Expo cloud build died hashing the JS bundle. I pinned Worklets Bundle Mode to false in Babel so Metro stopped hashing a graph I never asked for.

expoeasmetro+1

Read more

Reclaim Firebase orphan without password wipe

Sander Korf4 min read

An existing email is not always a blank account. I send a password reset when a login already exists, and I only set a password when none is on file.

firebaseexpo

Read more

eToro deploy vs rotate: idle cash not rotate

Sander Korf8 min read

A live eToro book sat in cash while the allocator returned success. I force a deploy, park leftover in indexes, and name the fee skip when a rotate waits.

etorotrading

Read more

eToro search 404: query fields, not ticker

Sander Korf4 min read

A live eToro rebalance returned 200 with zero orders. Search 404ed on a bad fields list. I keep the ticker map and treat an empty 200 as the miss.

etoroapiredis

Read more

Timed-out eToro opens are at-most-once

Sander Korf5 min read

A timeout on an eToro open can still fill the book. I treat open as at-most-once, leave closes on v1, and pick settlement from the matching leverage config.

etoroapi

Read more

eToro LLM overlay ±10pp: critic cannot veto

Sander Korf4 min read

A live eToro book went to cash when the model rejected the prompt. I keep a fully invested core, cap tilts at ten points, and ignore a critic on idle cash.

etorollmtypescript

Read more

Vercel Workflows: skip closed eToro legs

Sander Korf4 min read

When a market is closed, I skip that eToro leg instead of retrying the whole Vercel Workflows step. A retry after a partial fill can buy the same names twice.

verceletoroworkflows

Read more

Full Stack AI Engineer from Amsterdam

Sander Korf2 min read

I ship agentic Convex workflows and production Gen AI from Amsterdam. Posts cover what I build, who I work with, and what usually breaks in production.

welcomepersonal

Read more