[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"tag-tutorial-4":44,"search-suggestions":311,"tags-all":340},[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":308},[46,79,100,124,145,166,188,209,233,251,269,289],{"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":76,"playground":78,"hasQuiz":17,"hasPlayground":17},"01a066ea-3098-75c9-b7e5-1d5426f773eb","vue-weekly-composables-shared-state-trap","Vue Composables: The Shared State Trap (+ Cheat Sheet)",null,"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.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-composables-shared-state-trap.png",13,"2026-09-22T19:37:11.460Z",806,{"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},"vue","Vue",[66,67,70,73],{"slug":63,"name":64,"color":50},{"slug":68,"name":69,"color":50},"javascript","Javascript",{"slug":71,"name":72,"color":50},"tutorial","Tutorial",{"slug":74,"name":75,"color":50},"webdev","Webdev",{"assessments":77},1,{"slug":48},{"id":80,"slug":81,"title":82,"subtitle":50,"excerpt":83,"coverUrl":84,"locale":13,"readingMinutes":85,"publishedAt":86,"viewCount":87,"likeCount":19,"commentCount":19,"author":88,"vertical":89,"topic":90,"tags":93,"_count":98,"playground":99,"hasQuiz":17,"hasPlayground":17},"01a0b58a-7f56-736a-babf-1796c7b70c19","nextjs-weekly-middleware-to-proxy-network-boundary","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.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-middleware-to-proxy-network-boundary.png",12,"2026-09-22T19:36:08.416Z",992,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":91,"name":92},"nextjs","Nextjs",[94,95,96,97],{"slug":91,"name":92,"color":50},{"slug":68,"name":69,"color":50},{"slug":71,"name":72,"color":50},{"slug":74,"name":75,"color":50},{"assessments":77},{"slug":81},{"id":101,"slug":102,"title":103,"subtitle":50,"excerpt":104,"coverUrl":105,"locale":13,"readingMinutes":106,"publishedAt":107,"viewCount":108,"likeCount":19,"commentCount":19,"author":109,"vertical":110,"topic":111,"tags":114,"_count":122,"playground":123,"hasQuiz":17,"hasPlayground":17},"01a0af18-24b4-73ad-95ed-c9479ae0e91f","css-dynamic-viewport-units-dvh-svh-lvh","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.","\u002Fmedia\u002Fcovers\u002Fcss-dynamic-viewport-units-dvh-svh-lvh.png",6,"2026-09-22T19:35:37.351Z",668,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":112,"name":113},"css","CSS",[115,117,118,121],{"slug":112,"name":116,"color":50},"Css",{"slug":74,"name":75,"color":50},{"slug":119,"name":120,"color":50},"frontend","Frontend",{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":102},{"id":125,"slug":126,"title":127,"subtitle":50,"excerpt":128,"coverUrl":129,"locale":13,"readingMinutes":130,"publishedAt":131,"viewCount":132,"likeCount":19,"commentCount":19,"author":133,"vertical":134,"topic":135,"tags":138,"_count":143,"playground":144,"hasQuiz":17,"hasPlayground":17},"01a0ab57-dda2-74dd-816b-a0ef7f77aeb3","react-weekly-derived-state-bug","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.","\u002Fmedia\u002Fcovers\u002Freact-weekly-derived-state-bug.png",14,"2026-09-20T16:27:07.271Z",785,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":136,"name":137},"react","React",[139,140,141,142],{"slug":136,"name":137,"color":50},{"slug":68,"name":69,"color":50},{"slug":71,"name":72,"color":50},{"slug":74,"name":75,"color":50},{"assessments":77},{"slug":126},{"id":146,"slug":147,"title":148,"subtitle":50,"excerpt":149,"coverUrl":150,"locale":13,"readingMinutes":106,"publishedAt":151,"viewCount":152,"likeCount":19,"commentCount":19,"author":153,"vertical":154,"topic":155,"tags":157,"_count":164,"playground":165,"hasQuiz":17,"hasPlayground":17},"01a09fda-b750-710a-9a40-8113c1d054b3","drag-drop-api-dragover-preventdefault-trap","Your Browser Is Rejecting Every Drop On Purpose","A drop zone with a correctly wired drop listener that never fires — no error, no typo, nothing in the console. The browser is silently refusing the drop by design, and only one specific line stops it.","\u002Fmedia\u002Fcovers\u002Fdrag-drop-api-dragover-preventdefault-trap.png","2026-09-20T16:26:35.914Z",561,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":68,"name":156},"JavaScript",[158,159,160,163],{"slug":68,"name":69,"color":50},{"slug":74,"name":75,"color":50},{"slug":161,"name":162,"color":50},"html","Html",{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":147},{"id":167,"slug":168,"title":169,"subtitle":50,"excerpt":170,"coverUrl":171,"locale":13,"readingMinutes":130,"publishedAt":172,"viewCount":173,"likeCount":19,"commentCount":19,"author":174,"vertical":175,"topic":176,"tags":179,"_count":186,"playground":187,"hasQuiz":17,"hasPlayground":17},"01a0b07e-f66a-736f-a8f4-3c87da225d8f","nuxt-weekly-hydration-mismatch","Nuxt Hydration Mismatch: Why It Happens and How to Fix It","A Nuxt hydration mismatch happens when the server's HTML disagrees with the client's first render. Learn why, and the fixes that actually work.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-hydration-mismatch.png","2026-09-20T16:26:05.191Z",681,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":177,"name":178},"nuxt","Nuxt",[180,181,184,185],{"slug":177,"name":178,"color":50},{"slug":182,"name":183,"color":50},"ssr","SSR",{"slug":68,"name":69,"color":50},{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":168},{"id":189,"slug":190,"title":191,"subtitle":50,"excerpt":192,"coverUrl":193,"locale":13,"readingMinutes":194,"publishedAt":195,"viewCount":196,"likeCount":19,"commentCount":19,"author":197,"vertical":198,"topic":199,"tags":200,"_count":207,"playground":208,"hasQuiz":17,"hasPlayground":17},"01a0a4d0-bf0a-73a8-a83e-7e87ab5df792","eyedropper-api-native-color-picker","Chrome Already Has The Eyedropper You're Building","You're about to build a canvas-based color picker with a magnifier loupe. Chrome shipped a native one years ago — and it can sample pixels your own canvas can't even see.","\u002Fmedia\u002Fcovers\u002Feyedropper-api-native-color-picker.png",5,"2026-09-20T16:25:34.048Z",468,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":68,"name":156},[201,202,203,206],{"slug":68,"name":69,"color":50},{"slug":74,"name":75,"color":50},{"slug":204,"name":205,"color":50},"api","API",{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":190},{"id":210,"slug":211,"title":212,"subtitle":50,"excerpt":213,"coverUrl":214,"locale":13,"readingMinutes":53,"publishedAt":215,"viewCount":216,"likeCount":19,"commentCount":19,"author":217,"vertical":218,"topic":219,"tags":222,"_count":231,"playground":232,"hasQuiz":17,"hasPlayground":17},"01a0a630-c856-72d3-bae5-3339e38a8564","nestjs-weekly-testing-provider-overrides","NestJS Testing Module: Provider Overrides (with Cheat Sheet)","A complete guide to NestJS's testing module: how overrideProvider really wires mocks, why get() fails on scoped providers, and using useMocker.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-testing-provider-overrides.png","2026-09-18T17:23:03.842Z",766,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":220,"name":221},"nestjs","Nestjs",[223,224,227,230],{"slug":220,"name":221,"color":50},{"slug":225,"name":226,"color":50},"node","Node",{"slug":228,"name":229,"color":50},"typescript","Typescript",{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":211},{"id":234,"slug":235,"title":236,"subtitle":50,"excerpt":237,"coverUrl":238,"locale":13,"readingMinutes":130,"publishedAt":239,"viewCount":240,"likeCount":19,"commentCount":19,"author":241,"vertical":242,"topic":243,"tags":244,"_count":249,"playground":250,"hasQuiz":17,"hasPlayground":17},"01a09181-2f3b-7349-b651-fd9cdda0b931","nextjs-weekly-parallel-intercepting-routes-modals","Next.js Parallel & Intercepting Routes: Modals Done Right","How Next.js parallel routes (@slot) and intercepting routes ((.), (..), (...)) combine to build shareable, refreshable modals — verified against Next.js 16.3.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-parallel-intercepting-routes-modals.png","2026-09-15T17:50:47.218Z",748,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":91,"name":92},[245,246,247,248],{"slug":91,"name":92,"color":50},{"slug":136,"name":137,"color":50},{"slug":74,"name":75,"color":50},{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":235},{"id":252,"slug":253,"title":254,"subtitle":50,"excerpt":255,"coverUrl":256,"locale":13,"readingMinutes":53,"publishedAt":257,"viewCount":258,"likeCount":19,"commentCount":19,"author":259,"vertical":260,"topic":261,"tags":262,"_count":267,"playground":268,"hasQuiz":17,"hasPlayground":17},"01a04446-5a00-7624-b775-c73c6c035e67","vue-weekly-nexttick-batching-dom-updates","Vue nextTick Explained: How DOM Updates Are Batched","How Vue batches reactive writes into one DOM update, why the DOM looks stale right after a change, and how nextTick and watch's flush option fix it.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-nexttick-batching-dom-updates.png","2026-09-14T12:19:00.606Z",822,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[263,264,265,266],{"slug":63,"name":64,"color":50},{"slug":68,"name":69,"color":50},{"slug":71,"name":72,"color":50},{"slug":74,"name":75,"color":50},{"assessments":77},{"slug":253},{"id":270,"slug":271,"title":272,"subtitle":50,"excerpt":273,"coverUrl":274,"locale":13,"readingMinutes":194,"publishedAt":275,"viewCount":276,"likeCount":19,"commentCount":19,"author":277,"vertical":278,"topic":279,"tags":280,"_count":287,"playground":288,"hasQuiz":17,"hasPlayground":17},"01a085d3-d892-7065-956a-2f646cc7107e","speech-synthesis-api-browser-tts","The 'Read Aloud' Button That Only Works on the Second Click","A text-to-speech button that works perfectly every time you test it — and silently fails or picks the wrong voice for a real visitor's first click. The bug is a timing race baked into the Web Speech API that most code never accounts for.","\u002Fmedia\u002Fcovers\u002Fspeech-synthesis-api-browser-tts.png","2026-09-14T12:18:29.717Z",582,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":68,"name":156},[281,282,283,286],{"slug":68,"name":69,"color":50},{"slug":74,"name":75,"color":50},{"slug":284,"name":285,"color":50},"accessibility","Accessibility",{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":271},{"id":290,"slug":291,"title":292,"subtitle":50,"excerpt":293,"coverUrl":294,"locale":13,"readingMinutes":295,"publishedAt":296,"viewCount":297,"likeCount":19,"commentCount":19,"author":298,"vertical":299,"topic":300,"tags":301,"_count":306,"playground":307,"hasQuiz":17,"hasPlayground":17},"01a08c59-464c-7039-acf3-b32a0b9ee7ce","nuxt-weekly-useasyncdata-keys-dedupe","useAsyncData Keys in Nuxt: Caching, Dedupe & the Sharing Bug","Learn how Nuxt's useAsyncData and useFetch generate cache keys, how dedupe (cancel vs defer) really works, and why wrapper composables silently share data.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-useasyncdata-keys-dedupe.png",15,"2026-09-13T11:26:28.014Z",660,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":177,"name":178},[302,303,304,305],{"slug":177,"name":178,"color":50},{"slug":182,"name":183,"color":50},{"slug":68,"name":69,"color":50},{"slug":71,"name":72,"color":50},{"assessments":77},{"slug":291},{"page":309,"perPage":85,"total":310,"totalPages":106},4,71,[312,314,316,318,319,321,325,327,329,333,337,339],{"slug":74,"name":75,"articles":313},174,{"slug":68,"name":69,"articles":315},141,{"slug":119,"name":120,"articles":317},96,{"slug":71,"name":72,"articles":310},{"slug":112,"name":116,"articles":320},51,{"slug":322,"name":323,"articles":324},"performance","Performance",30,{"slug":228,"name":229,"articles":326},25,{"slug":136,"name":137,"articles":328},20,{"slug":330,"name":331,"articles":332},"programming","Programming",19,{"slug":334,"name":335,"articles":336},"browser","Browser",18,{"slug":225,"name":226,"articles":338},16,{"slug":161,"name":162,"articles":85},[341,342,343,344,345,346,347,348,349,350,351,352,353,355,357,358,360,362,365,368,372,375,378,381,382,385,388,389,392,395,398,401,404,407,410,413,416,419,422,425],{"slug":74,"name":75,"color":50,"articles":313},{"slug":68,"name":69,"color":50,"articles":315},{"slug":119,"name":120,"color":50,"articles":317},{"slug":71,"name":72,"color":50,"articles":310},{"slug":112,"name":116,"color":50,"articles":320},{"slug":322,"name":323,"color":50,"articles":324},{"slug":228,"name":229,"color":50,"articles":326},{"slug":136,"name":137,"color":50,"articles":328},{"slug":330,"name":331,"color":50,"articles":332},{"slug":334,"name":335,"color":50,"articles":336},{"slug":225,"name":226,"color":50,"articles":338},{"slug":161,"name":162,"color":50,"articles":85},{"slug":91,"name":92,"color":50,"articles":354},11,{"slug":63,"name":64,"color":50,"articles":356},10,{"slug":284,"name":285,"color":50,"articles":356},{"slug":220,"name":221,"color":50,"articles":359},9,{"slug":177,"name":178,"color":50,"articles":361},8,{"slug":363,"name":364,"color":50,"articles":361},"career","Career",{"slug":366,"name":367,"color":50,"articles":361},"discuss","Discuss",{"slug":369,"name":370,"color":50,"articles":371},"beginners","Beginners",7,{"slug":373,"name":374,"color":50,"articles":371},"productivity","Productivity",{"slug":376,"name":377,"color":50,"articles":106},"grammar","Grammar",{"slug":379,"name":380,"color":50,"articles":194},"security","Security",{"slug":182,"name":183,"color":50,"articles":309},{"slug":383,"name":384,"color":50,"articles":309},"apis","Apis",{"slug":386,"name":387,"color":50,"articles":43},"ai","Ai",{"slug":204,"name":205,"color":50,"articles":43},{"slug":390,"name":391,"color":50,"articles":43},"caching","Caching",{"slug":393,"name":394,"color":50,"articles":31},"async","Async",{"slug":396,"name":397,"color":50,"articles":31},"forms","Forms",{"slug":399,"name":400,"color":50,"articles":31},"dom","Dom",{"slug":402,"name":403,"color":50,"articles":31},"backend","Backend",{"slug":405,"name":406,"color":50,"articles":31},"english","English",{"slug":408,"name":409,"color":50,"articles":31},"verbs","Verbs",{"slug":411,"name":412,"color":50,"articles":31},"spanish","Spanish",{"slug":414,"name":415,"color":50,"articles":31},"a1","A1",{"slug":417,"name":418,"color":50,"articles":31},"a2","A2",{"slug":420,"name":421,"color":50,"articles":31},"tenses","Tenses",{"slug":423,"name":424,"color":50,"articles":31},"turkish","Turkish",{"slug":426,"name":427,"color":50,"articles":31},"animation","Animation"]