[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"tag-webdev-11":44,"search-suggestions":302,"tags-all":335},[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":297},[46,80,101,121,142,162,180,200,221,239,259,277],{"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},"019ff19b-f9ca-772e-8f09-cd3d3c640d2b","web-locks-api-tab-coordination","Five tabs open, one refresh token — the race nobody noticed",null,"Users with multiple tabs open triggered a silent race: every tab refreshed the same expiring auth token at once, and the server started invalidating its own sessions. navigator.locks fixes it with a real mutex, no polling or localStorage hacks required.","\u002Fmedia\u002Fcovers\u002Fweb-locks-api-tab-coordination.png",5,"2026-08-15T06:44:54.917Z",1326,{"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},"webdev","Webdev",{"slug":72,"name":73,"color":50},"frontend","Frontend",{"slug":75,"name":76,"color":50},"async","Async",{"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":92,"_count":99,"playground":100,"hasQuiz":17,"hasPlayground":17},"019fe660-8bc3-7444-8c96-86953902f5e0","findlast-search-from-end","You copy and reverse the array to find the last match. `findLast()` searches from the end directly.","The usual workaround — `[...arr].reverse().find()` — allocates a full copy just to walk it backward. ES2023 added `findLast()` and `findLastIndex()` to search from the end without touching the original.","\u002Fmedia\u002Fcovers\u002Ffindlast-search-from-end.png",4,"2026-08-14T07:38:34.486Z",832,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[93,94,95,96],{"slug":63,"name":67,"color":50},{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":97,"name":98,"color":50},"typescript","Typescript",{"assessments":78},{"slug":82},{"id":102,"slug":103,"title":104,"subtitle":50,"excerpt":105,"coverUrl":106,"locale":13,"readingMinutes":53,"publishedAt":107,"viewCount":108,"likeCount":19,"commentCount":19,"author":109,"vertical":110,"topic":111,"tags":112,"_count":119,"playground":120,"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",1209,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[113,114,115,116],{"slug":63,"name":67,"color":50},{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":117,"name":118,"color":50},"performance","Performance",{"assessments":78},{"slug":103},{"id":122,"slug":123,"title":124,"subtitle":50,"excerpt":125,"coverUrl":126,"locale":13,"readingMinutes":86,"publishedAt":127,"viewCount":128,"likeCount":19,"commentCount":19,"author":129,"vertical":130,"topic":131,"tags":134,"_count":140,"playground":141,"hasQuiz":17,"hasPlayground":17},"019ff19b-f763-70e0-abdf-eded59c8e69a","overscroll-behavior-scroll-containment","You're blocking touchmove events to contain scroll. `overscroll-behavior` does it natively.","When a scrollable container hits its edge, the page behind it starts scrolling too. Developers block this with JavaScript event handlers or body overflow tricks. overscroll-behavior is the CSS property that turns off scroll chaining in one line.","\u002Fmedia\u002Fcovers\u002Foverscroll-behavior-scroll-containment.png","2026-08-12T07:38:53.786Z",970,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":132,"name":133},"css","CSS",[135,137,138,139],{"slug":132,"name":136,"color":50},"Css",{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":63,"name":67,"color":50},{"assessments":78},{"slug":123},{"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},"019ff19b-f68d-77ea-9a2c-70e14c5c7a05","mutation-observer-dom-change-detection","You're polling setInterval to detect DOM changes. `MutationObserver` fires when they happen.","Watching for DOM mutations with setInterval or custom event dispatch is fragile and imprecise. The MutationObserver API delivers a callback exactly when attributes, text content, or child elements change — with full control over which types of mutations you watch.","\u002Fmedia\u002Fcovers\u002Fmutation-observer-dom-change-detection.png","2026-08-11T15:48:01.781Z",1010,{"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":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":158,"name":159,"color":50},"dom","Dom",{"assessments":78},{"slug":144},{"id":163,"slug":164,"title":165,"subtitle":50,"excerpt":166,"coverUrl":167,"locale":13,"readingMinutes":86,"publishedAt":168,"viewCount":169,"likeCount":19,"commentCount":19,"author":170,"vertical":171,"topic":172,"tags":173,"_count":178,"playground":179,"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",869,{"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":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":117,"name":118,"color":50},{"assessments":78},{"slug":164},{"id":181,"slug":182,"title":183,"subtitle":50,"excerpt":184,"coverUrl":185,"locale":13,"readingMinutes":86,"publishedAt":186,"viewCount":187,"likeCount":19,"commentCount":19,"author":188,"vertical":189,"topic":190,"tags":191,"_count":198,"playground":199,"hasQuiz":17,"hasPlayground":17},"019fe660-fcbe-745e-a443-5ae33ac10aaf","broadcast-channel-cross-tab-messaging","Stop using the localStorage hack to sync browser tabs. BroadcastChannel does it natively.","Syncing state across browser tabs with localStorage events is a widely-used trick that requires JSON.stringify, event filtering, and careful cleanup. The Broadcast Channel API delivers messages between tabs directly, with none of the side effects.","\u002Fmedia\u002Fcovers\u002Fbroadcast-channel-cross-tab-messaging.png","2026-08-09T07:02:41.629Z",1924,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[192,193,194,195],{"slug":63,"name":67,"color":50},{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":196,"name":197,"color":50},"apis","Apis",{"assessments":78},{"slug":182},{"id":201,"slug":202,"title":203,"subtitle":50,"excerpt":204,"coverUrl":205,"locale":13,"readingMinutes":53,"publishedAt":206,"viewCount":207,"likeCount":19,"commentCount":19,"author":208,"vertical":209,"topic":210,"tags":213,"_count":219,"playground":220,"hasQuiz":17,"hasPlayground":17},"019ff19b-f388-73f8-aa5b-93d73337d6fd","dialog-element-native-modal","You built your modal with a `\u003Cdiv>` and a focus trap library. The native `\u003Cdialog>` does all of that.","The `\u003Cdialog>` element ships with focus trapping, Escape-to-close, backdrop rendering, and correct ARIA semantics — no library, no boilerplate. It's been Baseline since 2022.","\u002Fmedia\u002Fcovers\u002Fdialog-element-native-modal.png","2026-08-08T07:00:10.810Z",980,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":211,"name":212},"html","HTML",[214,216,217,218],{"slug":211,"name":215,"color":50},"Html",{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":63,"name":67,"color":50},{"assessments":78},{"slug":202},{"id":222,"slug":223,"title":224,"subtitle":50,"excerpt":225,"coverUrl":226,"locale":13,"readingMinutes":86,"publishedAt":227,"viewCount":228,"likeCount":19,"commentCount":19,"author":229,"vertical":230,"topic":231,"tags":232,"_count":237,"playground":238,"hasQuiz":17,"hasPlayground":17},"019fe660-f552-73c8-9f52-d036e672c053","crypto-randomuuid-native-uuid","Stop importing `uuid`. `crypto.randomUUID()` has been native since 2021.","The `uuid` package is downloaded hundreds of millions of times a week. Most installs exist to call uuid\u002Fv4 — exactly what `crypto.randomUUID()` does natively, with no import, no bundle cost, and guaranteed cryptographic randomness.","\u002Fmedia\u002Fcovers\u002Fcrypto-randomuuid-native-uuid.png","2026-08-08T07:00:09.988Z",1000,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[233,234,235,236],{"slug":63,"name":67,"color":50},{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":117,"name":118,"color":50},{"assessments":78},{"slug":223},{"id":240,"slug":241,"title":242,"subtitle":50,"excerpt":243,"coverUrl":244,"locale":13,"readingMinutes":86,"publishedAt":245,"viewCount":246,"likeCount":19,"commentCount":19,"author":247,"vertical":248,"topic":249,"tags":250,"_count":257,"playground":258,"hasQuiz":17,"hasPlayground":17},"019fe660-edbb-714e-b0e8-8488ccbb6940","intl-relativetimeformat-relative-dates","You're installing date-fns for 'X hours ago'. `Intl.RelativeTimeFormat` does it natively.","Most apps display relative timestamps — '3 minutes ago', 'last week'. Developers still reach for date-fns or moment for this. The Intl.RelativeTimeFormat API handles it natively, with full locale support and zero dependencies.","\u002Fmedia\u002Fcovers\u002Fintl-relativetimeformat-relative-dates.png","2026-08-07T07:22:54.352Z",622,{"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":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":255,"name":256,"color":50},"intl","Intl",{"assessments":78},{"slug":241},{"id":260,"slug":261,"title":262,"subtitle":50,"excerpt":263,"coverUrl":264,"locale":13,"readingMinutes":86,"publishedAt":265,"viewCount":266,"likeCount":19,"commentCount":19,"author":267,"vertical":268,"topic":269,"tags":270,"_count":275,"playground":276,"hasQuiz":17,"hasPlayground":17},"019ff19b-f047-748a-800d-00912a4d95c9","object-hasown-safe-property-check","You use `obj.hasOwnProperty(key)` to check property ownership. It can silently fail. `Object.hasOwn()` is the safe replacement.","Two real cases where `obj.hasOwnProperty(key)` throws or returns wrong results — and why `Object.hasOwn(obj, key)` is the ES2022 fix that ESLint's `no-prototype-builtins` rule has been nudging you toward for years.","\u002Fmedia\u002Fcovers\u002Fobject-hasown-safe-property-check.png","2026-08-06T08:44:20.722Z",527,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},[271,272,273,274],{"slug":63,"name":67,"color":50},{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":97,"name":98,"color":50},{"assessments":78},{"slug":261},{"id":278,"slug":279,"title":280,"subtitle":50,"excerpt":281,"coverUrl":282,"locale":13,"readingMinutes":86,"publishedAt":283,"viewCount":284,"likeCount":19,"commentCount":19,"author":285,"vertical":286,"topic":287,"tags":288,"_count":295,"playground":296,"hasQuiz":17,"hasPlayground":17},"019fe660-e5d0-756a-9ae2-20c6ff4c2f48","css-style-queries-react-to-custom-properties","Container queries can check more than size — style queries let components read custom property values","Size queries react to container width. Style queries react to CSS custom property values on a container ancestor — enabling component-level theming and variant switching in pure CSS, no JavaScript involved.","\u002Fmedia\u002Fcovers\u002Fcss-style-queries-react-to-custom-properties.png","2026-08-06T08:44:20.136Z",584,{"id":57,"name":58,"username":59,"avatarUrl":50,"headline":60},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":132,"name":133},[289,290,291,292],{"slug":132,"name":136,"color":50},{"slug":69,"name":70,"color":50},{"slug":72,"name":73,"color":50},{"slug":293,"name":294,"color":50},"components","Components",{"assessments":78},{"slug":279},{"page":298,"perPage":299,"total":300,"totalPages":301},11,12,174,15,[303,304,306,308,312,314,316,318,322,326,330,334],{"slug":69,"name":70,"articles":300},{"slug":63,"name":67,"articles":305},141,{"slug":72,"name":73,"articles":307},96,{"slug":309,"name":310,"articles":311},"tutorial","Tutorial",71,{"slug":132,"name":136,"articles":313},51,{"slug":117,"name":118,"articles":315},30,{"slug":97,"name":98,"articles":317},25,{"slug":319,"name":320,"articles":321},"react","React",20,{"slug":323,"name":324,"articles":325},"programming","Programming",19,{"slug":327,"name":328,"articles":329},"browser","Browser",18,{"slug":331,"name":332,"articles":333},"node","Node",16,{"slug":211,"name":215,"articles":299},[336,337,338,339,340,341,342,343,344,345,346,347,348,351,355,358,362,366,369,372,376,379,383,386,389,390,393,396,399,400,403,404,407,410,413,416,419,422,425,428],{"slug":69,"name":70,"color":50,"articles":300},{"slug":63,"name":67,"color":50,"articles":305},{"slug":72,"name":73,"color":50,"articles":307},{"slug":309,"name":310,"color":50,"articles":311},{"slug":132,"name":136,"color":50,"articles":313},{"slug":117,"name":118,"color":50,"articles":315},{"slug":97,"name":98,"color":50,"articles":317},{"slug":319,"name":320,"color":50,"articles":321},{"slug":323,"name":324,"color":50,"articles":325},{"slug":327,"name":328,"color":50,"articles":329},{"slug":331,"name":332,"color":50,"articles":333},{"slug":211,"name":215,"color":50,"articles":299},{"slug":349,"name":350,"color":50,"articles":298},"nextjs","Nextjs",{"slug":352,"name":353,"color":50,"articles":354},"vue","Vue",10,{"slug":356,"name":357,"color":50,"articles":354},"accessibility","Accessibility",{"slug":359,"name":360,"color":50,"articles":361},"nestjs","Nestjs",9,{"slug":363,"name":364,"color":50,"articles":365},"nuxt","Nuxt",8,{"slug":367,"name":368,"color":50,"articles":365},"career","Career",{"slug":370,"name":371,"color":50,"articles":365},"discuss","Discuss",{"slug":373,"name":374,"color":50,"articles":375},"beginners","Beginners",7,{"slug":377,"name":378,"color":50,"articles":375},"productivity","Productivity",{"slug":380,"name":381,"color":50,"articles":382},"grammar","Grammar",6,{"slug":384,"name":385,"color":50,"articles":53},"security","Security",{"slug":387,"name":388,"color":50,"articles":86},"ssr","SSR",{"slug":196,"name":197,"color":50,"articles":86},{"slug":391,"name":392,"color":50,"articles":43},"ai","Ai",{"slug":394,"name":395,"color":50,"articles":43},"api","API",{"slug":397,"name":398,"color":50,"articles":43},"caching","Caching",{"slug":75,"name":76,"color":50,"articles":31},{"slug":401,"name":402,"color":50,"articles":31},"forms","Forms",{"slug":158,"name":159,"color":50,"articles":31},{"slug":405,"name":406,"color":50,"articles":31},"backend","Backend",{"slug":408,"name":409,"color":50,"articles":31},"english","English",{"slug":411,"name":412,"color":50,"articles":31},"verbs","Verbs",{"slug":414,"name":415,"color":50,"articles":31},"spanish","Spanish",{"slug":417,"name":418,"color":50,"articles":31},"a1","A1",{"slug":420,"name":421,"color":50,"articles":31},"a2","A2",{"slug":423,"name":424,"color":50,"articles":31},"tenses","Tenses",{"slug":426,"name":427,"color":50,"articles":31},"turkish","Turkish",{"slug":429,"name":430,"color":50,"articles":31},"animation","Animation"]