Zes kaarten op een dashboard, zes spinners, en ze verschijnen niet samen — ze verschijnen de één na de ander, als dominostenen, want elk begon pas te laden nadat het erboven klaar was. Dat is een waterval, en de totale wachttijd is niet het langzaamste verzoek, maar de som van alle. use() en Suspense breken de keten — maar het interessantste is de machinerie eronder: wat een gesuspend component werkelijk gooit, en hoe HTML die buiten volgorde aankomt toch op de juiste plek belandt.
In het artikel over micro-interacties schreef ik dat transform en opacity animeren op de compositorthread, zodat zelfs een zware pagina de animatie van een knop niet hoeft te laten haperen. Dat klopt nog steeds – met één voorbehoud waar dat artikel over zweeg: als de hoofdthread geblokkeerd is door een lange JS-taak, krijgt de animatie misschien nooit de kans om te starten, omdat de klik-handler zelf in de wachtrij staat. INP is de metriek die precies dat gat meet – en scheduler.yield() is een van de weinige manieren om het echt te dichten.
Een lijst van duizend reacties rendert traag, dus grijp je naar react-window – en in ruil voor soepel scrollen verlies je Ctrl+F, het afdrukken van de pagina en gedeeltelijk de navigatie van schermlezers, omdat elementen buiten het venster fysiek ophouden te bestaan in de DOM. content-visibility lost hetzelfde probleem anders op: de elementen blijven in de DOM, de browser slaat alleen het kostbare layout- en paintwerk ervoor over totdat ze dicht bij het zichtbare gebied komen – en, verrassend genoeg, kan ze tijdelijk weer tonen zodra je er tekst in zoekt.
In het artikel over toegankelijkheid was een modal met display:none, die nog steeds in de accessibility-boom stond, een voorbeeld van een fout die ontstaat door een modal van nul op te bouwen met divs. De echte vraag is: waarom moest hij überhaupt van nul worden opgebouwd? Ik zoek uit wat de native <dialog> en het popover-attribuut gratis bieden – top layer, focus trap, light dismiss – en waar deze twee mechanismen zo sterk verschillen dat ze door elkaar halen zo naar een ontoegankelijke interface leidt.
De knop 'Vind ik leuk' reageert onmiddellijk, het hart vult zich, de teller loopt op – nog voordat de server ook maar heeft geantwoord. Dit is geen illusie van snelheid zoals in het artikel over micro-interacties, het gaat een stap verder: de interface toont een status die nog niet bestaat, en gaat ervan uit dat hij er zo aankomt. Ik zoek uit hoe je dit veilig bouwt – met een echte terugtrekking uit de leugen wanneer de aanname niet uitkomt – en waarom een handmatige setState plus catch een slechtere versie is van wat useOptimistic biedt.
'use server' oogt als syntactische suiker voor een fetch naar een API-route – en precies daarom behandelen de meeste implementaties het als een gewone functieaanroep. Dat is een vergissing met concrete gevolgen: elke functie gemarkeerd met 'use server' wordt een publiek eindpunt op het netwerk, en een formulier dat op dit mechanisme is gebouwd, werkt zelfs voordat JavaScript de kans heeft gehad om te laden. Ik zoek uit wat er echt achter deze directive gebeurt, en waar de valkuilen zitten die je in de documentatie niet meteen ziet.