[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"tag-performance-2":44,"search-suggestions":296,"tags-all":325},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"items":45,"meta":293},[46,80,104,122,142,162,180,199,219,238,257,275],{"id":47,"slug":48,"title":49,"subtitle":50,"excerpt":51,"coverUrl":52,"locale":13,"readingMinutes":53,"publishedAt":54,"viewCount":55,"likeCount":19,"commentCount":19,"author":56,"vertical":61,"topic":62,"tags":65,"_count":77,"playground":79,"hasQuiz":17,"hasPlayground":17},"01a10fd5-be9a-719b-8acf-e0e930c602f8","long-animation-frames-api","Your Lighthouse Score Is 100. Long Animation Frames Explain the Jank.",null,"Lighthouse says 100 and CI is green, but a user still says the filter button feels sluggish. The Long Animation Frames API names the exact script and phase that blocked the frame — here's how to read it instead of squinting at a flame chart.","\u002Fmedia\u002Fcovers\u002Flong-animation-frames-api.png",6,"2026-10-11T12:25:55.553Z",522,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},"javascript","JavaScript",[66,68,71,74],{"slug":63,"name":67,"color":50},"Javascript",{"slug":69,"name":70,"color":50},"performance","Performance",{"slug":72,"name":73,"color":50},"webdev","Webdev",{"slug":75,"name":76,"color":50},"programming","Programming",{"assessments":78},1,{"slug":48},{"id":81,"slug":82,"title":83,"subtitle":50,"excerpt":84,"coverUrl":85,"locale":13,"readingMinutes":86,"publishedAt":87,"viewCount":88,"likeCount":19,"commentCount":19,"author":89,"vertical":90,"topic":91,"tags":94,"_count":102,"playground":103,"hasQuiz":17,"hasPlayground":17},"01a10aaf-5ec4-700b-bbd2-5fc17c27d4e0","css-will-change-performance-hint","You Added `will-change` to Fix the Jank. You Made It Worse.","will-change: transform looks like a free performance switch. Applied broadly or left on permanently, it does the opposite — here's the narrow, temporary pattern that actually works.","\u002Fmedia\u002Fcovers\u002Fcss-will-change-performance-hint.png",5,"2026-10-10T12:12:00.282Z",448,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":92,"name":93},"css","CSS",[95,97,98,99],{"slug":92,"name":96,"color":50},"Css",{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":100,"name":101,"color":50},"frontend","Frontend",{"assessments":78},{"slug":82},{"id":105,"slug":106,"title":107,"subtitle":50,"excerpt":108,"coverUrl":109,"locale":13,"readingMinutes":53,"publishedAt":110,"viewCount":111,"likeCount":19,"commentCount":19,"author":112,"vertical":113,"topic":114,"tags":115,"_count":120,"playground":121,"hasQuiz":17,"hasPlayground":17},"01a0f0ef-d9b8-719b-97a3-fb43e840c4d1","bfcache-beforeunload-back-button","`beforeunload` Can Still Kill Your bfcache. Mount It Only When Dirty.","A `beforeunload` listener you left mounted forever can still quietly cost you bfcache eligibility — and an `unload` listener reliably will. Here's the pagehide\u002Fpageshow fix, and why 'mount it only when there's something to lose' is the durable rule.","\u002Fmedia\u002Fcovers\u002Fbfcache-beforeunload-back-button.png","2026-10-05T06:10:30.618Z",782,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[116,117,118,119],{"slug":72,"name":73,"color":50},{"slug":63,"name":67,"color":50},{"slug":69,"name":70,"color":50},{"slug":100,"name":101,"color":50},{"assessments":78},{"slug":106},{"id":123,"slug":124,"title":125,"subtitle":50,"excerpt":126,"coverUrl":127,"locale":13,"readingMinutes":86,"publishedAt":128,"viewCount":129,"likeCount":19,"commentCount":19,"author":130,"vertical":131,"topic":132,"tags":135,"_count":140,"playground":141,"hasQuiz":17,"hasPlayground":17},"01a0ec73-247c-7738-84ae-80c9d1d116a8","input-lag-bug-debounce-cant-fix","Your Search Box Lags. Debounce Hides It; `useTransition` Fixes It","A big list re-render was blocking every keystroke — debouncing just moved the freeze. useTransition tells React which update can wait.","\u002Fmedia\u002Fcovers\u002Finput-lag-bug-debounce-cant-fix.png","2026-10-03T11:21:55.564Z",475,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":133,"name":134},"react","React",[136,137,138,139],{"slug":133,"name":134,"color":50},{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":124},{"id":143,"slug":144,"title":145,"subtitle":50,"excerpt":146,"coverUrl":147,"locale":13,"readingMinutes":86,"publishedAt":148,"viewCount":149,"likeCount":19,"commentCount":19,"author":150,"vertical":151,"topic":152,"tags":153,"_count":160,"playground":161,"hasQuiz":17,"hasPlayground":17},"01a0ddae-453b-76e9-8652-04984e4978ad","indexeddb-localstorage-main-thread-blocking","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.","\u002Fmedia\u002Fcovers\u002Findexeddb-localstorage-main-thread-blocking.png","2026-09-26T12:25:07.879Z",594,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[154,155,156,157],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":69,"name":70,"color":50},{"slug":158,"name":159,"color":50},"tutorial","Tutorial",{"assessments":78},{"slug":144},{"id":163,"slug":164,"title":165,"subtitle":50,"excerpt":166,"coverUrl":167,"locale":13,"readingMinutes":53,"publishedAt":168,"viewCount":169,"likeCount":19,"commentCount":19,"author":170,"vertical":171,"topic":172,"tags":173,"_count":178,"playground":179,"hasQuiz":17,"hasPlayground":17},"01a0ddae-3baa-7278-a8e5-48e21a2fe826","requestidlecallback-can-wait-forever","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.","\u002Fmedia\u002Fcovers\u002Frequestidlecallback-can-wait-forever.png","2026-09-26T12:24:05.529Z",482,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[174,175,176,177],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":69,"name":70,"color":50},{"slug":100,"name":101,"color":50},{"assessments":78},{"slug":164},{"id":181,"slug":182,"title":183,"subtitle":50,"excerpt":184,"coverUrl":185,"locale":13,"readingMinutes":186,"publishedAt":187,"viewCount":188,"likeCount":19,"commentCount":19,"author":189,"vertical":190,"topic":191,"tags":192,"_count":197,"playground":198,"hasQuiz":17,"hasPlayground":17},"01a018ca-67d5-7020-bce5-9816c38a7a54","weekly-web-workers","Web Workers in JavaScript: The Complete Guide","Learn JavaScript Web Workers step by step: message passing, transferable objects, error handling, and a copy-paste cheat sheet for offloading work.","\u002Fmedia\u002Fcovers\u002Fweekly-web-workers.png",13,"2026-09-07T12:03:05.323Z",684,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[193,194,195,196],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":158,"name":159,"color":50},{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":182},{"id":200,"slug":201,"title":202,"subtitle":50,"excerpt":203,"coverUrl":204,"locale":13,"readingMinutes":86,"publishedAt":205,"viewCount":206,"likeCount":19,"commentCount":19,"author":207,"vertical":208,"topic":209,"tags":210,"_count":217,"playground":218,"hasQuiz":17,"hasPlayground":17},"01a057f2-68b2-7521-a373-0ea70eae6f55","page-visibility-api-tab-aware-timers","Your Timer Doesn't Know You Tabbed Away","window blur feels like the obvious way to detect an idle tab, and it's wrong in ways that only show up in production. The Page Visibility API answers the actual question — can anyone see this? — so polling, timers, and video stop wasting work the moment nobody's looking.","\u002Fmedia\u002Fcovers\u002Fpage-visibility-api-tab-aware-timers.png","2026-09-05T10:17:19.010Z",730,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[211,212,213,216],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":214,"name":215,"color":50},"browser","Browser",{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":201},{"id":220,"slug":221,"title":222,"subtitle":50,"excerpt":223,"coverUrl":224,"locale":13,"readingMinutes":225,"publishedAt":226,"viewCount":227,"likeCount":19,"commentCount":19,"author":228,"vertical":229,"topic":230,"tags":231,"_count":236,"playground":237,"hasQuiz":17,"hasPlayground":17},"019ff73a-fb5b-73ce-b725-2912167a7dde","weekly-debounce-and-throttle","Debounce and Throttle in JavaScript: The Complete Guide","Learn debounce and throttle in JavaScript step by step, with worked examples, edge cases, and a copy-paste cheat sheet for search, scroll, and resize.","\u002Fmedia\u002Fcovers\u002Fweekly-debounce-and-throttle.png",14,"2026-08-31T13:12:14.139Z",781,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[232,233,234,235],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":158,"name":159,"color":50},{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":221},{"id":239,"slug":240,"title":241,"subtitle":50,"excerpt":242,"coverUrl":243,"locale":13,"readingMinutes":244,"publishedAt":245,"viewCount":246,"likeCount":19,"commentCount":19,"author":247,"vertical":248,"topic":249,"tags":250,"_count":255,"playground":256,"hasQuiz":17,"hasPlayground":17},"019ff19b-fbc7-7069-b3ba-28ffd8fbf4d3","navigator-sendbeacon-reliable-analytics","Your fetch in beforeunload is silently killed. navigator.sendBeacon guarantees delivery.","When the page unloads, the browser may cancel any in-flight fetch or XHR before it completes. navigator.sendBeacon sends a POST the browser commits to delivering, even after the document is gone.","\u002Fmedia\u002Fcovers\u002Fnavigator-sendbeacon-reliable-analytics.png",4,"2026-08-22T06:47:21.180Z",917,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[251,252,253,254],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":100,"name":101,"color":50},{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":240},{"id":258,"slug":259,"title":260,"subtitle":50,"excerpt":261,"coverUrl":262,"locale":13,"readingMinutes":86,"publishedAt":263,"viewCount":264,"likeCount":19,"commentCount":19,"author":265,"vertical":266,"topic":267,"tags":268,"_count":273,"playground":274,"hasQuiz":17,"hasPlayground":17},"019fe661-0450-720a-ad6e-5eb995429cd7","sendbeacon-reliable-unload-data","Your `fetch()` in `beforeunload` is being silently dropped. Use `navigator.sendBeacon()`.","Browsers cancel in-flight network requests when a page unloads. The common fix — synchronous XHR — is deprecated. `navigator.sendBeacon()` is the correct, fire-and-forget API designed exactly for this case.","\u002Fmedia\u002Fcovers\u002Fsendbeacon-reliable-unload-data.png","2026-08-13T07:40:48.196Z",1206,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[269,270,271,272],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":100,"name":101,"color":50},{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":259},{"id":276,"slug":277,"title":278,"subtitle":50,"excerpt":279,"coverUrl":280,"locale":13,"readingMinutes":244,"publishedAt":281,"viewCount":282,"likeCount":19,"commentCount":19,"author":283,"vertical":284,"topic":285,"tags":286,"_count":291,"playground":292,"hasQuiz":17,"hasPlayground":17},"019ff19b-f5b3-7569-8cc8-22b1136aa9b2","compressionstream-native-gzip","You're importing pako to gzip data. `CompressionStream` does it natively.","pako is one of npm's most downloaded packages — pulled in by thousands of projects to compress data before storing or sending it. CompressionStream and DecompressionStream do the same job natively, in any modern browser and Node.js 18+, with no dependencies and native C-speed execution.","\u002Fmedia\u002Fcovers\u002Fcompressionstream-native-gzip.png","2026-08-10T07:49:27.000Z",868,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[287,288,289,290],{"slug":63,"name":67,"color":50},{"slug":72,"name":73,"color":50},{"slug":100,"name":101,"color":50},{"slug":69,"name":70,"color":50},{"assessments":78},{"slug":277},{"page":31,"perPage":294,"total":295,"totalPages":43},12,30,[297,299,301,303,305,307,308,312,314,316,318,322],{"slug":72,"name":73,"articles":298},174,{"slug":63,"name":67,"articles":300},141,{"slug":100,"name":101,"articles":302},96,{"slug":158,"name":159,"articles":304},71,{"slug":92,"name":96,"articles":306},51,{"slug":69,"name":70,"articles":295},{"slug":309,"name":310,"articles":311},"typescript","Typescript",25,{"slug":133,"name":134,"articles":313},20,{"slug":75,"name":76,"articles":315},19,{"slug":214,"name":215,"articles":317},18,{"slug":319,"name":320,"articles":321},"node","Node",16,{"slug":323,"name":324,"articles":294},"html","Html",[326,327,328,329,330,331,332,333,334,335,336,337,338,342,346,349,353,357,360,363,367,370,373,376,379,382,385,388,391,394,397,400,403,406,409,412,415,418,421,424],{"slug":72,"name":73,"color":50,"articles":298},{"slug":63,"name":67,"color":50,"articles":300},{"slug":100,"name":101,"color":50,"articles":302},{"slug":158,"name":159,"color":50,"articles":304},{"slug":92,"name":96,"color":50,"articles":306},{"slug":69,"name":70,"color":50,"articles":295},{"slug":309,"name":310,"color":50,"articles":311},{"slug":133,"name":134,"color":50,"articles":313},{"slug":75,"name":76,"color":50,"articles":315},{"slug":214,"name":215,"color":50,"articles":317},{"slug":319,"name":320,"color":50,"articles":321},{"slug":323,"name":324,"color":50,"articles":294},{"slug":339,"name":340,"color":50,"articles":341},"nextjs","Nextjs",11,{"slug":343,"name":344,"color":50,"articles":345},"vue","Vue",10,{"slug":347,"name":348,"color":50,"articles":345},"accessibility","Accessibility",{"slug":350,"name":351,"color":50,"articles":352},"nestjs","Nestjs",9,{"slug":354,"name":355,"color":50,"articles":356},"nuxt","Nuxt",8,{"slug":358,"name":359,"color":50,"articles":356},"career","Career",{"slug":361,"name":362,"color":50,"articles":356},"discuss","Discuss",{"slug":364,"name":365,"color":50,"articles":366},"beginners","Beginners",7,{"slug":368,"name":369,"color":50,"articles":366},"productivity","Productivity",{"slug":371,"name":372,"color":50,"articles":53},"grammar","Grammar",{"slug":374,"name":375,"color":50,"articles":86},"security","Security",{"slug":377,"name":378,"color":50,"articles":244},"ssr","SSR",{"slug":380,"name":381,"color":50,"articles":244},"apis","Apis",{"slug":383,"name":384,"color":50,"articles":43},"ai","Ai",{"slug":386,"name":387,"color":50,"articles":43},"api","API",{"slug":389,"name":390,"color":50,"articles":43},"caching","Caching",{"slug":392,"name":393,"color":50,"articles":31},"async","Async",{"slug":395,"name":396,"color":50,"articles":31},"forms","Forms",{"slug":398,"name":399,"color":50,"articles":31},"dom","Dom",{"slug":401,"name":402,"color":50,"articles":31},"backend","Backend",{"slug":404,"name":405,"color":50,"articles":31},"english","English",{"slug":407,"name":408,"color":50,"articles":31},"verbs","Verbs",{"slug":410,"name":411,"color":50,"articles":31},"spanish","Spanish",{"slug":413,"name":414,"color":50,"articles":31},"a1","A1",{"slug":416,"name":417,"color":50,"articles":31},"a2","A2",{"slug":419,"name":420,"color":50,"articles":31},"tenses","Tenses",{"slug":422,"name":423,"color":50,"articles":31},"turkish","Turkish",{"slug":425,"name":426,"color":50,"articles":31},"animation","Animation"]