<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/styles.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>JavaScript Development Space - Master JS and NodeJS</title><description>Explore the world of JavaScript at our blog, your ultimate resource for guides, tutorials, and articles. Uncover the latest insights, tips, and trends.</description><link>https://jsdev.space/</link><language>en-us</language><item><title>Friday Links #41: JavaScript, AI and Open Source</title><link>https://jsdev.space/friday/friday-41/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-41/</guid><description>The biggest JavaScript releases, AI developer tools, open source projects, browser updates, and engineering news from the past week.</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-41.png&quot; alt=&quot;Friday Links #41&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another busy week brought new JavaScript releases, fresh AI coding tools, browser improvements, and several open source projects that deserve attention. There were also important updates from the Node.js ecosystem, new developer utilities, and a handful of experiments that hint at where modern web development is heading next.&lt;/p&gt;
&lt;p&gt;As always, this issue collects the stories, libraries, tutorials, and launches that stood out. Whether you&apos;re building production applications, experimenting with AI-assisted development, or simply trying to keep up with the ecosystem, these are the links worth adding to your reading list.&lt;/p&gt;
&lt;h2&gt;🧠 Language &amp;amp; Runtime Updates&lt;/h2&gt;
&lt;h3&gt;TypeScript 7.0&lt;/h3&gt;
&lt;p&gt;The biggest release of the week is finally here. &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/&quot;&gt;TypeScript 7&lt;/a&gt; has officially landed, introducing the long-awaited Go-based compiler that&apos;s designed to dramatically reduce compilation times, especially for large codebases.&lt;/p&gt;
&lt;p&gt;It&apos;s still early days, though. Much of the ecosystem continues to target TypeScript 6 while tooling authors update plugins, language servers, and build integrations. If you&apos;re planning to upgrade, it&apos;s worth checking compatibility with your existing toolchain first.&lt;/p&gt;
&lt;h3&gt;npm 12&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.blog/changelog/2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation/&quot;&gt;npm has reached&lt;/a&gt; another major milestone with the release of version 12.&lt;/p&gt;
&lt;p&gt;The new version includes CLI improvements, dependency management updates, performance refinements, and plenty of internal cleanup. While there aren&apos;t many headline-grabbing features, it&apos;s an important release that lays the groundwork for future improvements across the Node.js ecosystem.&lt;/p&gt;
&lt;h3&gt;Bun 1.3.14&lt;/h3&gt;
&lt;p&gt;Bun continues its rapid release cadence. Version 1.3.14 focuses primarily on bug fixes, compatibility improvements, and runtime performance.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bun.com/blog/bun-in-rust&quot;&gt;The Bun team also published&lt;/a&gt; one of the most interesting engineering stories of the year: a detailed write-up explaining how the runtime is being rewritten from Zig to Rust using dozens of parallel Claude Code sessions. The Rust implementation will become the foundation of Bun 1.4.&lt;/p&gt;
&lt;h3&gt;Node.js 26.5.0&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/en/blog/release/v26.5.0&quot;&gt;Node.js 26&lt;/a&gt; continues to evolve with another feature-packed release. Version 26.5 adds support for importing text files using ESM import attributes, making it easier to work with non-JavaScript assets without custom loaders.&lt;/p&gt;
&lt;p&gt;The release also introduces &lt;code&gt;blob.textStream()&lt;/code&gt; along with several smaller improvements across the runtime, performance, and platform APIs.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.googleblog.com/litertjs-googles-high-performance-web-ai-inference/&quot;&gt;LiteRT.js, Google&apos;s high performance Web AI Inference&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.blog/security/6-security-settings-every-github-maintainer-should-enable-this-week/&quot;&gt;6 security settings every GitHub maintainer should enable this week&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jstools.space/blog/read-large-json-files/&quot;&gt;How to Read Large JSON Files Without Losing Your Mind&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://page-transitions-with-webgpu-vanilla-js.crnacura.workers.dev/&quot;&gt;Building Persistent Page Transitions with WebGPU and Vanilla JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://morello.dev/blog/solidjs-2-react-developers-first-look&quot;&gt;SolidJS 2.0: A React Developer&apos;s First Look at Signals and Async&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://olliewilliams.xyz/blog/no-build/&quot;&gt;Do we still need build tools?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.logrocket.com/css-staggered-animations/&quot;&gt;How to create awesome staggered animations in CSS&lt;/a&gt;, &lt;a href=&quot;https://github.com/color-js/color.js/releases/tag/v0.7.0&quot;&gt;Color.js v0.7.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jstools.space/blog/password-hashing-argon2/&quot;&gt;Password Hashing Done Right: Argon2, bcrypt, Salt and Pepper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/blog/next-security-release-program&quot;&gt;Next.js Security Release and Our Next Patch Release&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jstools.space/html-formatter/&quot;&gt;HTML Formatter Online&lt;/a&gt; - A simple online tool for formatting and beautifying HTML code. It supports various formatting options, including indentation styles, line breaks, and attribute sorting, making it easy to clean up messy HTML code.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.babylonjs.com/lite/&quot;&gt;Babylon Lite&lt;/a&gt;: A Fresh Take on Babylon.js — What if the Babylon.js team could rebuild their 3D engine from scratch today? That&apos;s exactly the idea behind Babylon Lite. Designed exclusively for WebGPU, the new engine is significantly smaller and faster than the original, thanks to a cleaner architecture and the removal of years of legacy compatibility. The trade-off is that some features haven&apos;t made the jump yet, but Babylon Lite offers a glimpse of what the next generation of web-based 3D engines could look like.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hyperblam.how/&quot;&gt;HYPERBLAM&lt;/a&gt; lets you make music with HTML&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mozilla/web-ext/releases/tag/10.5.0&quot;&gt;web-ext 10.5&lt;/a&gt; - A command line tool to help build, run, and test web extensions&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wordpalette.github.io/&quot;&gt;WordPalette&lt;/a&gt; – Generate a brand palette and visual identity from a word or image&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blur-unblur.github.io/&quot;&gt;Blur and Unblur Faces&lt;/a&gt; – A simple web tool that uses AI to blur or unblur faces in images. It can be useful for privacy protection, content moderation, or creative effects.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sindresorhus/eslint-node-test&quot;&gt;eslint-node-test&lt;/a&gt; — An ESLint plugin that catches common mistakes and enforces best practices when writing tests with Node.js&apos;s built-in &lt;code&gt;node:test&lt;/code&gt; runner.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nubjs.com/&quot;&gt;Nub&lt;/a&gt; — A TypeScript-first toolchain that runs TypeScript files, package.json scripts, and local CLIs using your existing Node.js installation, with no custom runtime or vendor lock-in.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://depsguard.com/&quot;&gt;DepsGuard&lt;/a&gt; — A native cross-platform security tool that scans and hardens npm, pnpm, Yarn, Bun, uv, pip, and Poetry configurations to reduce supply chain risks with a single command and zero dependencies.&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://rombo.co/tailwind/&quot;&gt;tailwindcss-motion&lt;/a&gt; - A new animation library for Tailwind CSS that adds more than 20 ready-to-use animations with a simple drop-in setup. Every effect can be previewed, tweaked, and customized directly in the browser, and the project even includes a visual animation builder for creating your own motion presets without writing complex CSS.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flueframework.com/&quot;&gt;Flue&lt;/a&gt; - The Open Agent Framework.  Build durable AI agents and workflows with Flue&apos;s programmable TypeScript harness. Write once, deploy anywhere, use any LLM.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://crustjs.com/&quot;&gt;CrustJS&lt;/a&gt; — A lightweight, dependency-free CLI framework built specifically for Bun and written with a TypeScript-first approach. It features a fluent builder API, composable modules, and a modern developer experience, making it easy to build fast, maintainable command-line applications without pulling in a large dependency tree.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://geajs.com/&quot;&gt;Gea&lt;/a&gt; — A compiler-first UI framework that shifts most of the work to build time instead of runtime. Rather than relying on a virtual DOM, hooks, or signals, Gea analyzes ordinary classes and functions during compilation and generates highly targeted DOM updates. The result is a lightweight runtime with minimal overhead while letting developers write familiar JavaScript and TypeScript code.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vercel.com/eve&quot;&gt;eve&lt;/a&gt; — A framework for building AI agents with a Next.js-like developer experience, combining Markdown-based instructions, Typ…</content:encoded></item><item><title>Master Docker Swarm with Real Production Examples</title><link>https://jsdev.space/docker-swarm-explained/</link><guid isPermaLink="true">https://jsdev.space/docker-swarm-explained/</guid><description>Learn how Docker Swarm works, how to build highly available clusters, deploy services, and manage production workloads with Docker&apos;s native orchestrator.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Containerization changed how applications are built and deployed. Running a single Docker container on one machine is straightforward, but production systems rarely stay that simple. As applications grow, they need redundancy, automated recovery, rolling deployments, service discovery, and the ability to scale across multiple servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docker Swarm&lt;/strong&gt; was created to solve exactly these problems.&lt;/p&gt;
&lt;p&gt;Unlike many orchestration platforms that introduce an entirely new ecosystem, Swarm extends Docker itself. If you already understand Docker images, containers, and networks, moving to Swarm feels like a natural next step instead of learning an entirely different platform.&lt;/p&gt;
&lt;p&gt;This guide explores how Docker Swarm works internally, how its architecture is organized, and why many teams continue to use it for production environments where simplicity is often more valuable than unlimited flexibility.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Docker Swarm Exists&lt;/h2&gt;
&lt;p&gt;A standalone Docker engine works perfectly for local development and small deployments. Everything runs on one machine, networking is simple, and container management is straightforward.&lt;/p&gt;
&lt;p&gt;Production infrastructure introduces new challenges.&lt;/p&gt;
&lt;p&gt;What happens if the server fails?&lt;/p&gt;
&lt;p&gt;How do you distribute traffic between multiple container replicas?&lt;/p&gt;
&lt;p&gt;How do you update an application without interrupting users?&lt;/p&gt;
&lt;p&gt;How can containers running on different physical servers communicate securely?&lt;/p&gt;
&lt;p&gt;Managing all of these concerns manually quickly becomes impractical.&lt;/p&gt;
&lt;p&gt;Docker Swarm groups multiple Docker hosts into a single logical cluster. Instead of thinking about individual machines, you manage services that the cluster automatically schedules across available nodes.&lt;/p&gt;
&lt;p&gt;The platform handles many operational tasks behind the scenes, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scheduling workloads&lt;/li&gt;
&lt;li&gt;restarting failed containers&lt;/li&gt;
&lt;li&gt;distributing traffic&lt;/li&gt;
&lt;li&gt;maintaining the desired number of replicas&lt;/li&gt;
&lt;li&gt;rolling out updates safely&lt;/li&gt;
&lt;li&gt;recovering from node failures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is an orchestration system that feels familiar to anyone already using Docker.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker Swarm vs Standalone Docker&lt;/h2&gt;
&lt;p&gt;A single Docker host and a Swarm cluster share the same Docker engine, but they solve very different problems.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Standalone Docker&lt;/th&gt;
&lt;th&gt;Docker Swarm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hosts&lt;/td&gt;
&lt;td&gt;One machine&lt;/td&gt;
&lt;td&gt;Multiple machines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High availability&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service discovery&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load balancing&lt;/td&gt;
&lt;td&gt;External tools&lt;/td&gt;
&lt;td&gt;Integrated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rolling updates&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster management&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Standalone Docker gives you complete control over one server.&lt;/p&gt;
&lt;p&gt;Docker Swarm provides centralized management for an entire fleet of Docker hosts.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where Swarm Fits in the Container Ecosystem&lt;/h2&gt;
&lt;p&gt;Several orchestration platforms exist today, each with different goals.&lt;/p&gt;
&lt;h3&gt;Docker Swarm&lt;/h3&gt;
&lt;p&gt;Docker Swarm focuses on simplicity.&lt;/p&gt;
&lt;p&gt;It integrates directly with Docker, requires very little additional tooling, and can often be deployed in minutes. Teams that already use Docker can begin clustering servers with minimal changes to their existing workflows.&lt;/p&gt;
&lt;h3&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;Kubernetes offers significantly more features.&lt;/p&gt;
&lt;p&gt;It includes advanced scheduling, custom controllers, autoscaling, extensive networking options, and one of the largest cloud-native ecosystems available today.&lt;/p&gt;
&lt;p&gt;The tradeoff is complexity.&lt;/p&gt;
&lt;p&gt;Learning Kubernetes requires understanding many new concepts that go far beyond containers themselves.&lt;/p&gt;
&lt;h3&gt;Nomad&lt;/h3&gt;
&lt;p&gt;Nomad, developed by HashiCorp, takes a different approach.&lt;/p&gt;
&lt;p&gt;It schedules containers alongside virtual machines, binaries, and other workloads while maintaining a relatively small operational footprint.&lt;/p&gt;
&lt;p&gt;Many organizations choose Nomad when they need flexibility beyond Docker containers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Understanding the Swarm Architecture&lt;/h2&gt;
&lt;p&gt;A Docker Swarm cluster consists of multiple servers working together as one distributed system.&lt;/p&gt;
&lt;p&gt;At a high level, every node belongs to one of two categories.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                 Docker Swarm Cluster

         ┌─────────────────────────────┐
         │        Manager Nodes        │
         │                             │
         │  Leader      Followers      │
         └──────────────┬──────────────┘
                        │
        ─────────────────────────────────
           │                       │
     Worker Node             Worker Node
           │                       │
        Containers            Containers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although every server runs Docker, not every machine performs the same responsibilities.&lt;/p&gt;
&lt;p&gt;Understanding these roles is essential before deploying a production cluster.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Manager Nodes&lt;/h2&gt;
&lt;p&gt;Manager nodes control the cluster.&lt;/p&gt;
&lt;p&gt;They maintain the desired state of every service, schedule workloads, monitor cluster health, and coordinate communication between all participating nodes.&lt;/p&gt;
&lt;p&gt;Whenever you create or update a service, the request is processed by a manager.&lt;/p&gt;
&lt;p&gt;Managers are responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cluster configuration&lt;/li&gt;
&lt;li&gt;scheduling decisions&lt;/li&gt;
&lt;li&gt;service updates&lt;/li&gt;
&lt;li&gt;node management&lt;/li&gt;
&lt;li&gt;certificate distribution&lt;/li&gt;
&lt;li&gt;maintaining cluster state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only managers can change the cluster configuration.&lt;/p&gt;
&lt;p&gt;Worker nodes execute tasks but cannot modify the cluster itself.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Leader Election&lt;/h2&gt;
&lt;p&gt;Manager nodes do not all perform identical work.&lt;/p&gt;
&lt;p&gt;One manager becomes the leader.&lt;/p&gt;
&lt;p&gt;The leader accepts write operations and coordinates changes throughout the cluster.&lt;/p&gt;
&lt;p&gt;Additional managers remain synchronized replicas.&lt;/p&gt;
&lt;p&gt;If the leader becomes unavailable, another manager automatically takes over without manual intervention.&lt;/p&gt;
&lt;p&gt;This election process is powered by the Raft consensus algorithm.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Raft Matters&lt;/h2&gt;
&lt;p&gt;Distributed systems need agreement.&lt;/p&gt;
&lt;p&gt;If multiple managers accepted conflicting updates simultaneously, the cluster would quickly become inconsistent.&lt;/p&gt;
&lt;p&gt;Raft solves this problem by ensuring every manager shares the same view of cluster state.&lt;/p&gt;
&lt;p&gt;Only one leader processes updates, while followers replicate the resulting state.&lt;/p&gt;
&lt;p&gt;For production environments, Docker recommends using an odd number of managers.&lt;/p&gt;
&lt;p&gt;Typical configurations include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3 managers&lt;/li&gt;
&lt;li&gt;5 managers&lt;/li&gt;
&lt;li&gt;7 managers for very large clusters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Odd numbers prevent split-brain scenarios and maximize fault tolerance.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Worker Nodes&lt;/h2&gt;
&lt;p&gt;Worker nodes are much simpler.&lt;/p&gt;
&lt;p&gt;Their primary responsibility is running containers assigned by managers.&lt;/p&gt;
&lt;p&gt;Workers do not participate in scheduling decisions.&lt;/p&gt;
&lt;p&gt;Instead, they:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;execute tasks&lt;/li&gt;
&lt;li&gt;report container status&lt;/li&gt;
&lt;li&gt;perform health checks&lt;/li&gt;
&lt;li&gt;download required images&lt;/li&gt;
&lt;li&gt;communicate results back to managers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This separation keeps the architecture clean.&lt;/p&gt;
&lt;p&gt;Managers coordinate.&lt;/p&gt;
&lt;p&gt;Workers execute.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Core Components Behind the Scenes&lt;/h2&gt;
&lt;p&gt;Several internal components work together every time a service is created.&lt;/p&gt;
&lt;h3&gt;Scheduler&lt;/h3&gt;
&lt;p&gt;The scheduler determines where workloads should run.&lt;/p&gt;
&lt;p&gt;It evaluates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;available CPU&lt;/li&gt;
&lt;li&gt;memory&lt;/li&gt;
&lt;li&gt;placement rules&lt;/li&gt;
&lt;li&gt;node availability&lt;/li&gt;
&lt;li&gt;labels&lt;/li&gt;
&lt;li&gt;resource reservations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After analyzing the cluster, it selects the most appropriate node for each task.&lt;/p&gt;
&lt;h…</content:encoded></item><item><title>Howto Use the Decorator Pattern in Front-End Development</title><link>https://jsdev.space/howto/decorator-pattern-frontend/</link><guid isPermaLink="true">https://jsdev.space/howto/decorator-pattern-frontend/</guid><description>Learn how decorators separate business logic from performance monitoring, retries, debouncing, permissions, and loading states.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The decorator pattern appears throughout modern front-end development, even when developers do not call it by name.&lt;/p&gt;
&lt;p&gt;Higher-order functions, React higher-order components, performance wrappers, retry utilities, access-control layers, and memoization helpers all follow a similar principle. They take existing behavior and add something around it without rewriting the original implementation.&lt;/p&gt;
&lt;p&gt;React&apos;s &lt;code&gt;memo&lt;/code&gt; function is a familiar example. It accepts a component and returns an enhanced version that can skip unnecessary renders.&lt;/p&gt;
&lt;p&gt;The same approach can be used for API monitoring, retry strategies, permission checks, loading indicators, debouncing, analytics, and error handling.&lt;/p&gt;
&lt;p&gt;This article explains the decorator pattern through practical JavaScript and React examples.&lt;/p&gt;
&lt;h2&gt;The Problem: Business Logic Mixed with Monitoring&lt;/h2&gt;
&lt;p&gt;Suppose an application needs to measure how long an API request takes.&lt;/p&gt;
&lt;p&gt;The most direct solution is to place timing code inside the request function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function fetchUsers() {
  const start = performance.now();

  const response = await fetch(&apos;/api/users&apos;);
  const data = await response.json();

  const duration = performance.now() - start;
  console.log(`Request completed in ${duration.toFixed(2)}ms`);

  return data;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function works, but it now has two responsibilities.&lt;/p&gt;
&lt;p&gt;It retrieves users and measures performance.&lt;/p&gt;
&lt;p&gt;That may not seem serious when there is only one request. The problem becomes clearer after adding &lt;code&gt;fetchOrders&lt;/code&gt;, &lt;code&gt;fetchProducts&lt;/code&gt;, &lt;code&gt;fetchInvoices&lt;/code&gt;, and several other API functions.&lt;/p&gt;
&lt;p&gt;Each function would need similar timing logic. Updating the logging format or changing the monitoring system would require editing every implementation.&lt;/p&gt;
&lt;p&gt;This is the kind of duplication the decorator pattern can remove.&lt;/p&gt;
&lt;p&gt;Performance tracking is a cross-cutting concern. It affects many parts of an application, but it is not part of the main business operation.&lt;/p&gt;
&lt;p&gt;Other common cross-cutting concerns include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Error reporting&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Debouncing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of placing these behaviors inside every function, decorators keep them in reusable wrappers.&lt;/p&gt;
&lt;h2&gt;What Is the Decorator Pattern?&lt;/h2&gt;
&lt;p&gt;The decorator pattern adds new responsibilities to an existing function, object, or component without changing its original source code.&lt;/p&gt;
&lt;p&gt;In JavaScript, a decorator is often implemented as a function that accepts another function and returns a new one.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function withExtraBehavior(fn) {
  return function (...args) {
    // Run additional behavior before the original function.

    const result = fn(...args);

    // Run additional behavior after the original function.

    return result;
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The returned function preserves the original behavior while adding another layer around it.&lt;/p&gt;
&lt;p&gt;Front-end applications commonly use two forms of this pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Higher-order functions that decorate ordinary JavaScript functions.&lt;/li&gt;
&lt;li&gt;Higher-order components that decorate React components.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Modern React applications often prefer custom Hooks and component composition over higher-order components. However, HOCs remain useful in libraries, route guards, legacy codebases, and reusable component infrastructure.&lt;/p&gt;
&lt;h2&gt;Decorating Functions with Higher-Order Functions&lt;/h2&gt;
&lt;p&gt;A higher-order function accepts another function, returns a function, or does both.&lt;/p&gt;
&lt;p&gt;This makes higher-order functions a natural way to implement decorators in JavaScript.&lt;/p&gt;
&lt;h2&gt;Adding Performance Monitoring&lt;/h2&gt;
&lt;p&gt;The timing logic from the previous example can be extracted into a reusable decorator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function withPerformance(fn, label = fn.name || &apos;anonymous&apos;) {
  return async function (...args) {
    const start = performance.now();

    try {
      return await fn.apply(this, args);
    } finally {
      const duration = performance.now() - start;

      console.log(
        `[${label}] completed in ${duration.toFixed(2)}ms`,
      );
    }
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The original API function can now focus only on its main responsibility:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function fetchUsers() {
  const response = await fetch(&apos;/api/users&apos;);

  if (!response.ok) {
    throw new Error(`Request failed with status ${response.status}`);
  }

  return response.json();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a decorated version when performance monitoring is required:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const monitoredFetchUsers = withPerformance(
  fetchUsers,
  &apos;Fetch users&apos;,
);

const users = await monitoredFetchUsers();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The decorator runs the original function, measures its execution time, and reports the result.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;finally&lt;/code&gt; block ensures that timing information is recorded even when the request fails.&lt;/p&gt;
&lt;p&gt;The use of &lt;code&gt;fn.apply(this, args)&lt;/code&gt; also preserves the original function&apos;s context. This matters when decorating object methods that depend on &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The original &lt;code&gt;fetchUsers&lt;/code&gt; function remains unchanged and can still be used without monitoring.&lt;/p&gt;
&lt;h2&gt;Adding a Retry Mechanism&lt;/h2&gt;
&lt;p&gt;Network requests can fail because of temporary connectivity problems, rate limits, overloaded services, or unstable third-party APIs.&lt;/p&gt;
&lt;p&gt;Retry logic can also be implemented as a decorator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function wait(duration) {
  return new Promise((resolve) =&amp;gt; {
    setTimeout(resolve, duration);
  });
}

function withRetry(
  fn,
  {
    retries = 3,
    delay = 1000,
    shouldRetry = () =&amp;gt; true,
  } = {},
) {
  return async function (...args) {
    let lastError;

    for (let attempt = 0; attempt &amp;lt;= retries; attempt += 1) {
      try {
        return await fn.apply(this, args);
      } catch (error) {
        lastError = error;

        const reachedLastAttempt = attempt === retries;

        if (reachedLastAttempt || !shouldRetry(error)) {
          throw error;
        }

        await wait(delay);
      }
    }

    throw lastError;
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The request can now gain retry behavior without knowing anything about retry rules:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const resilientFetchUsers = withRetry(fetchUsers, {
  retries: 3,
  delay: 800,
});

const users = await resilientFetchUsers();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The decorator is reusable because it is not tied to &lt;code&gt;fetchUsers&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const resilientFetchOrders = withRetry(fetchOrders, {
  retries: 2,
  delay: 500,
});

const resilientSaveProduct = withRetry(saveProduct, {
  retries: 4,
  delay: 1200,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A retry decorator should not automatically repeat every failed operation.&lt;/p&gt;
&lt;p&gt;For example, retrying a request after a &lt;code&gt;401 Unauthorized&lt;/code&gt; response is usually pointless. Retrying a payment request may also be dangerous if the endpoint is not idempotent.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;shouldRetry&lt;/code&gt; option allows the caller to decide which failures are temporary:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const resilientFetchUsers = withRetry(fetchUsers, {
  retries: 3,
  delay: 1000,
  shouldRetry(error) {
    return error instanceof TypeError;
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The strategy can later be replaced with exponential backoff without modifying every API function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function withExponentialRetry(fn, retries = 3) {
  return async function (...args) {
    for (let attempt = 0; attempt &amp;lt;= retries; attempt += 1) {
      try {
        return await fn.apply(this, args);
      } catch (error) {
        if (attempt === retries) {
          throw error;
        }

        const delay = 500 * 2 ** attempt;
        await wait(delay);
      }
    }
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Centralizing the retry policy makes it easier to test, maintain, and change.&lt;/p&gt;
&lt;h2&gt;Decorating Functions with Debouncing&lt;/h2&gt;
&lt;p&gt;Search fi…</content:encoded></item><item><title>JavaScript String Algorithms Every Developer Should Know</title><link>https://jsdev.space/javascript-string-algorithms/</link><guid isPermaLink="true">https://jsdev.space/javascript-string-algorithms/</guid><description>Learn common JavaScript string interview questions: reversing strings, detecting palindromes, using two pointers, and optimizing practical solutions.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;String algorithms are among the most common topics in JavaScript interviews.&lt;/p&gt;
&lt;p&gt;At first glance, they seem straightforward. Reverse a string, check whether it&apos;s a palindrome, or remove a character. None of these problems look particularly difficult.&lt;/p&gt;
&lt;p&gt;However, these exercises teach much more than simple string manipulation. They reveal how well you understand JavaScript fundamentals, immutable data structures, algorithmic thinking, and time and space complexity.&lt;/p&gt;
&lt;p&gt;In this article, we&apos;ll explore three classic interview questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reversing a string&lt;/li&gt;
&lt;li&gt;Checking whether a string is a palindrome&lt;/li&gt;
&lt;li&gt;Determining whether removing at most one character can produce a palindrome&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Along the way, you&apos;ll also learn why the two-pointer technique is one of the most valuable patterns in algorithm design.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Reversing a String&lt;/h2&gt;
&lt;p&gt;The classic problem is simple.&lt;/p&gt;
&lt;p&gt;Given:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;abc&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Return:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;cba&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike arrays, JavaScript strings don&apos;t have a built-in &lt;code&gt;reverse()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;This doesn&apos;t work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const str = &quot;abc&quot;;

// TypeError
str.reverse();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, the standard solution converts the string into an array.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const str = &quot;abc&quot;;

const reversed = str
  .split(&quot;&quot;)
  .reverse()
  .join(&quot;&quot;);

console.log(reversed); // &quot;cba&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The process consists of three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;split(&quot;&quot;)&lt;/code&gt; converts the string into an array.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reverse()&lt;/code&gt; reverses the array.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;join(&quot;&quot;)&lt;/code&gt; combines everything back into a string.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This solution is clean, easy to read, and runs in &lt;strong&gt;O(n)&lt;/strong&gt; time.&lt;/p&gt;
&lt;p&gt;Its space complexity is also &lt;strong&gt;O(n)&lt;/strong&gt; because a new array is created.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Does a String Have &lt;code&gt;.length&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;JavaScript strings are primitive values.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typeof &quot;hello&quot;;
// &quot;string&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yet they still behave like objects.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const str = &quot;hello&quot;;

console.log(str.length);
console.log(str[0]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How is that possible?&lt;/p&gt;
&lt;p&gt;Whenever you access a property or method on a primitive string, JavaScript temporarily wraps it in a &lt;code&gt;String&lt;/code&gt; object behind the scenes.&lt;/p&gt;
&lt;p&gt;Conceptually, it&apos;s similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const temp = new String(&quot;hello&quot;);

temp.length;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The wrapper object exists only for the duration of the operation before being discarded.&lt;/p&gt;
&lt;p&gt;This behavior lets primitive values expose useful methods while remaining lightweight.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Understanding &lt;code&gt;call()&lt;/code&gt; and &lt;code&gt;this&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Another interview favorite involves &lt;code&gt;Function.prototype.call()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consider this example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const person = {
  name: &quot;Alice&quot;,

  say() {
    console.log(this.name);
  },
};

const anotherPerson = {
  name: &quot;Bob&quot;,
};

person.say();

person.say.call(anotherPerson);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first call prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Alice
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;call()&lt;/code&gt; doesn&apos;t copy the function.&lt;/p&gt;
&lt;p&gt;It simply changes what &lt;code&gt;this&lt;/code&gt; refers to during execution.&lt;/p&gt;
&lt;p&gt;This same mechanism powers one of JavaScript&apos;s most reliable type-checking techniques.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Object.prototype.toString.call([]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[object Array]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because &lt;code&gt;toString()&lt;/code&gt; executes with a different &lt;code&gt;this&lt;/code&gt;, it can inspect many different object types.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Checking for a Palindrome&lt;/h2&gt;
&lt;p&gt;A palindrome reads the same in both directions.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;level
racecar
abba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The simplest solution reverses the string and compares it with the original.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function isPalindrome(str) {
  return str ===
    str
      .split(&quot;&quot;)
      .reverse()
      .join(&quot;&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although easy to understand, this approach creates another string and requires extra memory.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Better Solution: Two Pointers&lt;/h2&gt;
&lt;p&gt;A palindrome is symmetrical.&lt;/p&gt;
&lt;p&gt;Instead of reversing the entire string, compare characters from both ends.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function isPalindrome(str) {
  let left = 0;
  let right = str.length - 1;

  while (left &amp;lt; right) {
    if (str[left] !== str[right]) {
      return false;
    }

    left++;
    right--;
  }

  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution still runs in &lt;strong&gt;O(n)&lt;/strong&gt; time.&lt;/p&gt;
&lt;p&gt;However, it only uses &lt;strong&gt;O(1)&lt;/strong&gt; extra memory because no additional arrays or strings are created.&lt;/p&gt;
&lt;p&gt;The two-pointer technique appears in countless interview questions involving:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;symmetric data&lt;/li&gt;
&lt;li&gt;sorted arrays&lt;/li&gt;
&lt;li&gt;substrings&lt;/li&gt;
&lt;li&gt;intervals&lt;/li&gt;
&lt;li&gt;partitioning&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Learning it early pays off repeatedly.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Remove One Character to Form a Palindrome&lt;/h2&gt;
&lt;p&gt;Now let&apos;s make the problem slightly harder.&lt;/p&gt;
&lt;p&gt;Given a string, determine whether removing &lt;strong&gt;at most one character&lt;/strong&gt; makes it a palindrome.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;aba&quot;  → true

&quot;abca&quot; → true

&quot;abc&quot;  → false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two-pointer approach still works.&lt;/p&gt;
&lt;p&gt;Whenever the left and right characters match, continue toward the center.&lt;/p&gt;
&lt;p&gt;If they don&apos;t match, only two possibilities remain.&lt;/p&gt;
&lt;p&gt;Skip the left character.&lt;/p&gt;
&lt;p&gt;Or skip the right one.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function validPalindrome(str) {

  function isPalindrome(left, right) {

    while (left &amp;lt; right) {

      if (str[left] !== str[right]) {
        return false;
      }

      left++;
      right--;
    }

    return true;
  }

  let left = 0;
  let right = str.length - 1;

  while (left &amp;lt; right) {

    if (str[left] !== str[right]) {

      return (
        isPalindrome(left + 1, right) ||
        isPalindrome(left, right - 1)
      );
    }

    left++;
    right--;
  }

  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The helper function lets us verify both possibilities without duplicating code.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Only Two Cases?&lt;/h2&gt;
&lt;p&gt;Suppose these two characters don&apos;t match.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;... x ........ y ...
    ↑          ↑
   left      right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If removing one character can solve the problem, then one of these mismatched characters must disappear.&lt;/p&gt;
&lt;p&gt;Removing any other character won&apos;t fix the current mismatch.&lt;/p&gt;
&lt;p&gt;Therefore, we only need to test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;isPalindrome(left + 1, right);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;isPalindrome(left, right - 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing else needs to be checked.&lt;/p&gt;
&lt;p&gt;That&apos;s why the algorithm remains linear instead of becoming quadratic.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Complexity Analysis&lt;/h2&gt;
&lt;h3&gt;Reverse String&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Time: &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Space: &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Palindrome (Two Pointers)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Time: &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Space: &lt;strong&gt;O(1)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Remove One Character&lt;/h3&gt;
&lt;p&gt;Although an additional palindrome check may occur after finding a mismatch, every character is still visited only a constant number of times.&lt;/p&gt;
&lt;p&gt;The complexity remains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Time: &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Space: &lt;strong&gt;O(1)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Good Habits When Solving String Problems&lt;/h2&gt;
&lt;p&gt;As you practice more interview questions, several patterns emerge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remember that strings are immutable.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most operations create new strings rather than modifying existing ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prefer two pointers whenever possible.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;They&apos;re often faster and require less memory than repeatedly creating arrays or substrings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extract reusable logic.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Small helper functions usually make algorithms easier to understand and easier to test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Always think about edge cases.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Test your solution with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;empty strings&lt;/li&gt;
&lt;li&gt;single-character strings&lt;/…</content:encoded></item><item><title>Observer vs Publish-Subscribe: What&apos;s the Difference?</title><link>https://jsdev.space/observer-vs-publish-subscribe-pattern/</link><guid isPermaLink="true">https://jsdev.space/observer-vs-publish-subscribe-pattern/</guid><description>Learn the real difference between the Observer and Publish-Subscribe patterns. Compare implementations, understand trade-offs, and see practical examples.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you&apos;ve been building frontend applications for a while, you&apos;ve probably encountered both the &lt;strong&gt;Observer&lt;/strong&gt; pattern and the &lt;strong&gt;Publish-Subscribe (Pub/Sub)&lt;/strong&gt; pattern.&lt;/p&gt;
&lt;p&gt;They often look almost identical. Both involve listeners, callbacks, and notifications. Both allow multiple parts of an application to react when something changes.&lt;/p&gt;
&lt;p&gt;Because of these similarities, developers frequently use the names interchangeably. In reality, they solve different problems and lead to different application architectures.&lt;/p&gt;
&lt;p&gt;Understanding where they differ helps you design applications that are easier to scale, test, and maintain.&lt;/p&gt;
&lt;p&gt;Let&apos;s compare both patterns using the same real-world example.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Common Frontend Scenario&lt;/h2&gt;
&lt;p&gt;Imagine you&apos;re building a real-time stock dashboard.&lt;/p&gt;
&lt;p&gt;A WebSocket connection continuously receives market updates.&lt;/p&gt;
&lt;p&gt;Several independent parts of the application need those updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a price chart&lt;/li&gt;
&lt;li&gt;a watchlist&lt;/li&gt;
&lt;li&gt;a notification panel&lt;/li&gt;
&lt;li&gt;an activity log&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The WebSocket should not care who consumes the data. It simply receives new market information.&lt;/p&gt;
&lt;p&gt;There are two common ways to distribute those updates.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Option 1: The Observer Pattern&lt;/h2&gt;
&lt;p&gt;With the Observer pattern, the subject directly keeps track of every observer.&lt;/p&gt;
&lt;p&gt;Whenever new data arrives, it loops through every registered observer and calls them one by one.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Subject {
  #observers = new Set();

  subscribe(observer) {
    this.#observers.add(observer);
    return () =&amp;gt; this.#observers.delete(observer);
  }

  notify(payload) {
    this.#observers.forEach(observer =&amp;gt; observer(payload));
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage is straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ticker = new Subject();

const drawChart = (tick) =&amp;gt; {};
const flashRow = (tick) =&amp;gt; {};
const logTick = (tick) =&amp;gt; {};

ticker.subscribe(drawChart);
ticker.subscribe(flashRow);
ticker.subscribe(logTick);

socket.addEventListener(&quot;message&quot;, event =&amp;gt; {
  ticker.notify(JSON.parse(event.data));
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the &lt;code&gt;ticker&lt;/code&gt; object knows every consumer.&lt;/p&gt;
&lt;p&gt;Whenever new market data arrives, it actively notifies each observer.&lt;/p&gt;
&lt;h3&gt;Characteristics&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The subject owns the list of observers.&lt;/li&gt;
&lt;li&gt;Every observer receives every update.&lt;/li&gt;
&lt;li&gt;Consumers are directly connected to the data source.&lt;/li&gt;
&lt;li&gt;Adding a new observer only requires calling &lt;code&gt;subscribe()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This creates a clear one-to-many relationship between a single source of truth and its consumers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Option 2: Publish-Subscribe&lt;/h2&gt;
&lt;p&gt;The Publish-Subscribe pattern introduces an intermediary.&lt;/p&gt;
&lt;p&gt;Instead of sending updates directly to consumers, publishers emit events into an event bus.&lt;/p&gt;
&lt;p&gt;Subscribers simply listen for the events they care about.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class EventBus {
  #events = {};

  on(event, callback) {
    (this.#events[event] ||= []).push(callback);
    return () =&amp;gt; this.off(event, callback);
  }

  off(event, callback) {
    this.#events[event] =
      this.#events[event]?.filter(cb =&amp;gt; cb !== callback);
  }

  emit(event, ...args) {
    this.#events[event]?.forEach(cb =&amp;gt; cb(...args));
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the event bus looks very similar.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const bus = new EventBus();

bus.on(&quot;tick&quot;, drawChart);
bus.on(&quot;tick&quot;, flashRow);
bus.on(&quot;tick&quot;, logTick);

socket.addEventListener(&quot;message&quot;, event =&amp;gt; {
  bus.emit(&quot;tick&quot;, JSON.parse(event.data));
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important difference is that the WebSocket never communicates with the consumers directly.&lt;/p&gt;
&lt;p&gt;It only emits an event.&lt;/p&gt;
&lt;p&gt;The event bus decides who receives it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Key Difference&lt;/h2&gt;
&lt;p&gt;At first glance, both implementations produce the same result.&lt;/p&gt;
&lt;p&gt;The architecture, however, is completely different.&lt;/p&gt;
&lt;h3&gt;Observer&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;WebSocket
      │
      ▼
   Subject
      │
 ┌────┼────┐
 ▼    ▼    ▼
Chart List Logger
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The subject knows every observer.&lt;/p&gt;
&lt;p&gt;Communication is direct.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Publish-Subscribe&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;WebSocket
      │
      ▼
  Event Bus
      │
 ┌────┼────┐
 ▼    ▼    ▼
Chart List Logger
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Neither the publisher nor the subscribers know about each other.&lt;/p&gt;
&lt;p&gt;The event bus sits between them.&lt;/p&gt;
&lt;p&gt;This extra layer creates much looser coupling.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where You&apos;ll Find the Observer Pattern&lt;/h2&gt;
&lt;p&gt;Many frontend APIs are built around Observer semantics.&lt;/p&gt;
&lt;h3&gt;React State Subscriptions&lt;/h3&gt;
&lt;p&gt;React doesn&apos;t implement the classic Observer pattern internally in the same way as Vue, but the pattern appears throughout the React ecosystem.&lt;/p&gt;
&lt;p&gt;When a component subscribes to an external store, it automatically re-renders whenever the relevant state changes. Libraries like Redux, Zustand, MobX, Jotai, and React&apos;s own &lt;code&gt;useSyncExternalStore&lt;/code&gt; API all rely on this idea.&lt;/p&gt;
&lt;p&gt;Each store maintains a list of subscribers. When the state changes, it notifies those subscribers, allowing React to update only the components that depend on the changed data.&lt;/p&gt;
&lt;h2&gt;The relationship is still the same: a single source of state owns its subscribers and informs them whenever its data changes.&lt;/h2&gt;
&lt;h3&gt;Browser Observer APIs&lt;/h3&gt;
&lt;p&gt;Modern browsers also provide several observer-based APIs.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IntersectionObserver&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MutationObserver&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ResizeObserver&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each watches a specific target and triggers callbacks whenever that target changes.&lt;/p&gt;
&lt;p&gt;Again, the observed object directly manages its observers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;State Management Libraries&lt;/h3&gt;
&lt;p&gt;Several state management solutions also follow Observer principles.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MobX&lt;/li&gt;
&lt;li&gt;Signals&lt;/li&gt;
&lt;li&gt;SolidJS reactivity&lt;/li&gt;
&lt;li&gt;Angular Signals&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of them maintain explicit relationships between observable state and dependent computations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where Publish-Subscribe Shines&lt;/h2&gt;
&lt;p&gt;Pub/Sub becomes much more useful when modules should remain independent.&lt;/p&gt;
&lt;p&gt;A publisher shouldn&apos;t know who is listening.&lt;/p&gt;
&lt;p&gt;A subscriber shouldn&apos;t know who produced the event.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Cross-Component Communication&lt;/h2&gt;
&lt;p&gt;Lightweight event buses are common in both React and Vue applications.&lt;/p&gt;
&lt;p&gt;One popular choice is &lt;strong&gt;mitt&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import mitt from &quot;mitt&quot;;

type Events = {
  &quot;user-login&quot;: {
    userId: string;
    name: string;
  };
};

export const eventBus = mitt&amp;lt;Events&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Publishing an event:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eventBus.emit(&quot;user-login&quot;, {
  userId: user.id,
  name: user.name,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Listening for it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  const handler = data =&amp;gt; setUser(data);

  eventBus.on(&quot;user-login&quot;, handler);

  return () =&amp;gt; {
    eventBus.off(&quot;user-login&quot;, handler);
  };
}, []);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The login component doesn&apos;t know who reacts to the event.&lt;/p&gt;
&lt;p&gt;The navigation bar doesn&apos;t know where the event originated.&lt;/p&gt;
&lt;p&gt;Both only communicate through the event bus.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Micro-Frontends&lt;/h3&gt;
&lt;p&gt;Micro-frontend architectures frequently use Pub/Sub.&lt;/p&gt;
&lt;p&gt;Independent applications often communicate through shared event buses rather than importing each other&apos;s code directly.&lt;/p&gt;
&lt;p&gt;This keeps teams independent while allowing applications to exchange information.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Custom Events&lt;/h3&gt;
&lt;p&gt;The browser&apos;s own &lt;code&gt;CustomEvent&lt;/code&gt; system is another example of Publish-Subscribe.&lt;/p&gt;
&lt;p&gt;Components dispatch events.&lt;/p&gt;
&lt;p&gt;Other components listen for them.&lt;/p&gt;
&lt;p&gt;Neither side depends on the other.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Common Mistake: Forgetting to Unsubscribe&lt;/h2&gt;
&lt;p&gt;Both patterns rely on callbacks.&lt;/p&gt;
&lt;p&gt;That means both can create memory leaks.&lt;/p&gt;
&lt;p&gt;If callbacks remain registered after a component has been destroyed, the JavaScript gar…</content:encoded></item><item><title>How to Protect HTML Content from Being Copied</title><link>https://jsdev.space/howto/protect-from-copying/</link><guid isPermaLink="true">https://jsdev.space/howto/protect-from-copying/</guid><description>Learn what actually works to discourage copying content in HTML. Explore CSS, JavaScript, Canvas, clipboard APIs, font obfuscation.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every few months, someone asks the same question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;How can I stop users from copying my website?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The short answer is simple.&lt;/p&gt;
&lt;p&gt;You can&apos;t.&lt;/p&gt;
&lt;p&gt;If a browser can display your content, a user can eventually copy it.&lt;/p&gt;
&lt;p&gt;The only thing frontend code can do is increase the effort required.&lt;/p&gt;
&lt;p&gt;Whether that&apos;s enough depends on who you&apos;re trying to stop. Casual visitors, curious developers, and professional scrapers all require very different levels of protection.&lt;/p&gt;
&lt;p&gt;Understanding this difference is the key to choosing the right approach.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Fundamental Problem&lt;/h2&gt;
&lt;p&gt;Every anti-copy solution starts with the same limitation.&lt;/p&gt;
&lt;p&gt;Before a browser can display text, it has to download and render that content.&lt;/p&gt;
&lt;p&gt;Once users can see it, they have multiple ways to extract it.&lt;/p&gt;
&lt;p&gt;They can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;copy the text&lt;/li&gt;
&lt;li&gt;inspect the HTML&lt;/li&gt;
&lt;li&gt;capture a screenshot&lt;/li&gt;
&lt;li&gt;run OCR on the screenshot&lt;/li&gt;
&lt;li&gt;use browser extensions&lt;/li&gt;
&lt;li&gt;automate the browser with scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As long as people can read the content, there will always be a way to obtain it.&lt;/p&gt;
&lt;p&gt;That means anti-copy techniques don&apos;t answer the question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can users copy this?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead they answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How difficult is it to copy?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Level 1: CSS Protection&lt;/h2&gt;
&lt;p&gt;The simplest approach disables text selection.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.protected {
  user-select: none;
  -webkit-user-select: none;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users can&apos;t highlight text, which also prevents the usual &lt;strong&gt;Ctrl+C&lt;/strong&gt; workflow.&lt;/p&gt;
&lt;p&gt;This is probably the most common anti-copy technique on the web.&lt;/p&gt;
&lt;h3&gt;How easy is it to bypass?&lt;/h3&gt;
&lt;p&gt;Very easy.&lt;/p&gt;
&lt;p&gt;Open DevTools and remove the CSS rule.&lt;/p&gt;
&lt;p&gt;Or simply execute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.querySelector(&quot;.protected&quot;)
  .style.userSelect = &quot;text&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Text selection immediately works again.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Disable the Context Menu&lt;/h2&gt;
&lt;p&gt;Another popular trick blocks the right-click menu.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.addEventListener(
  &quot;contextmenu&quot;,
  event =&amp;gt; event.preventDefault()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without the context menu, users lose quick access to &lt;strong&gt;Copy&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, they also lose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Translate&lt;/li&gt;
&lt;li&gt;Inspect&lt;/li&gt;
&lt;li&gt;Dictionary lookup&lt;/li&gt;
&lt;li&gt;Accessibility tools&lt;/li&gt;
&lt;li&gt;Browser extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The downside often outweighs the protection.&lt;/p&gt;
&lt;p&gt;Most browsers also allow users to disable websites from blocking the context menu.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Level 2: JavaScript Event Blocking&lt;/h2&gt;
&lt;p&gt;Instead of preventing selection, JavaScript can intercept the copy operation itself.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.addEventListener(
  &quot;copy&quot;,
  event =&amp;gt; {
    event.preventDefault();
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pressing &lt;strong&gt;Ctrl+C&lt;/strong&gt; no longer places anything on the clipboard.&lt;/p&gt;
&lt;p&gt;This is slightly stronger than CSS.&lt;/p&gt;
&lt;p&gt;It is still easy to bypass.&lt;/p&gt;
&lt;p&gt;A developer can register another event listener before yours or simply disable JavaScript entirely.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Invisible Overlay Layers&lt;/h2&gt;
&lt;p&gt;Some websites place a transparent element above the content.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.overlay {
  position: absolute;
  inset: 0;
  z-index: 999;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users interact with the overlay instead of the text underneath.&lt;/p&gt;
&lt;p&gt;Again, DevTools defeats the technique almost instantly.&lt;/p&gt;
&lt;p&gt;Deleting the overlay or changing &lt;code&gt;pointer-events&lt;/code&gt; restores normal interaction.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Level 3: Allow Copying, but Modify It&lt;/h2&gt;
&lt;p&gt;Instead of fighting the user, many large websites take a different approach.&lt;/p&gt;
&lt;p&gt;They allow copying.&lt;/p&gt;
&lt;p&gt;They simply modify what gets copied.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.addEventListener(
  &quot;copy&quot;,
  event =&amp;gt; {

    event.preventDefault();

    const text =
      window.getSelection().toString();

    event.clipboardData.setData(
      &quot;text/plain&quot;,
      text +
      &quot;\n\nSource: example.com&quot;
    );
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users still get their selected text.&lt;/p&gt;
&lt;p&gt;However, every paste now includes a source attribution.&lt;/p&gt;
&lt;p&gt;This strategy has become popular because it preserves usability while encouraging proper attribution.&lt;/p&gt;
&lt;p&gt;It&apos;s far less frustrating than disabling copying altogether.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Level 4: Don&apos;t Store Text as Text&lt;/h2&gt;
&lt;p&gt;Eventually developers realize that every DOM node can be inspected.&lt;/p&gt;
&lt;p&gt;The next idea is simple.&lt;/p&gt;
&lt;p&gt;Don&apos;t place text inside the DOM.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Render Everything with Canvas&lt;/h3&gt;
&lt;p&gt;Instead of HTML, draw text directly onto a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ctx =
  canvas.getContext(&quot;2d&quot;);

ctx.font = &quot;16px sans-serif&quot;;

ctx.fillText(
  &quot;Confidential&quot;,
  20,
  40
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users see readable content.&lt;/p&gt;
&lt;p&gt;The HTML contains almost nothing.&lt;/p&gt;
&lt;p&gt;Traditional scrapers receive an empty page.&lt;/p&gt;
&lt;p&gt;There are serious trade-offs.&lt;/p&gt;
&lt;p&gt;Search engines cannot index the content.&lt;/p&gt;
&lt;p&gt;Screen readers lose access.&lt;/p&gt;
&lt;p&gt;Rendering becomes more expensive.&lt;/p&gt;
&lt;p&gt;Responsive layouts become harder.&lt;/p&gt;
&lt;p&gt;Attackers can still use OCR or intercept drawing commands.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Font Obfuscation&lt;/h2&gt;
&lt;p&gt;One of the strongest frontend techniques uses custom fonts.&lt;/p&gt;
&lt;p&gt;The HTML stores meaningless characters.&lt;/p&gt;
&lt;p&gt;The font maps those characters to completely different glyphs.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTML:
xk#2m

Displayed:
hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When users copy the text, they receive the original encoded characters instead of what appeared on screen.&lt;/p&gt;
&lt;p&gt;This technique is commonly used by premium document platforms and online reading services.&lt;/p&gt;
&lt;p&gt;It is significantly harder to bypass.&lt;/p&gt;
&lt;p&gt;However, it still isn&apos;t impossible.&lt;/p&gt;
&lt;p&gt;A determined attacker can analyze the font mapping or simply rely on OCR.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Choose Protection Based on the Threat&lt;/h2&gt;
&lt;p&gt;Not every website needs military-grade protection.&lt;/p&gt;
&lt;p&gt;Think about who you&apos;re trying to stop.&lt;/p&gt;
&lt;h3&gt;Casual visitors&lt;/h3&gt;
&lt;p&gt;Simple CSS and basic JavaScript usually discourage accidental copying.&lt;/p&gt;
&lt;h3&gt;Developers&lt;/h3&gt;
&lt;p&gt;Browser DevTools bypass nearly every client-side protection within seconds.&lt;/p&gt;
&lt;h3&gt;Professional scrapers&lt;/h3&gt;
&lt;p&gt;Automation tools combined with OCR can eventually extract almost any visible content.&lt;/p&gt;
&lt;p&gt;The difference is time.&lt;/p&gt;
&lt;p&gt;The stronger your protection, the longer extraction takes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Don&apos;t Sacrifice User Experience&lt;/h2&gt;
&lt;p&gt;Many anti-copy techniques create bigger problems than they solve.&lt;/p&gt;
&lt;p&gt;Blocking selection prevents users from quoting your articles.&lt;/p&gt;
&lt;p&gt;Disabling right-click breaks browser features people rely on every day.&lt;/p&gt;
&lt;p&gt;Rendering everything inside Canvas hurts SEO and accessibility.&lt;/p&gt;
&lt;p&gt;Before adding protection, ask yourself an important question.&lt;/p&gt;
&lt;p&gt;Are you trying to stop content theft, or are you making your own website harder to use?&lt;/p&gt;
&lt;p&gt;In many cases, adding attribution to copied text provides a better balance than preventing copying entirely.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Which Technique Should You Choose?&lt;/h2&gt;
&lt;p&gt;For most websites, a layered approach works best.&lt;/p&gt;
&lt;p&gt;Start with lightweight protection.&lt;/p&gt;
&lt;p&gt;Add clipboard attribution.&lt;/p&gt;
&lt;p&gt;Avoid techniques that harm accessibility or search indexing unless your content genuinely requires stronger protection.&lt;/p&gt;
&lt;p&gt;If you&apos;re publishing premium documents or paid educational material, Canvas rendering or font obfuscation may provide additional resistance.&lt;/p&gt;
&lt;p&gt;Just remember that these techniques delay copying.&lt;/p&gt;
&lt;p&gt;They don&apos;t eliminate it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;The perfect anti-copy solution doesn&apos;t exist.&lt;/p&gt;
&lt;p&gt;Every piece of content displayed in a browser can eventually be extracted.&lt;/p&gt;
&lt;p&gt;The goal is not to build an unbreakable system.&lt;/p&gt;
&lt;p&gt;The goal is to raise the cost of copying high enough that casual users stop trying while legitimate visitors continue enjoying a …</content:encoded></item><item><title>Howto Replace if-Else with the Strategy Pattern</title><link>https://jsdev.space/howto/strategy-pattern-javascript/</link><guid isPermaLink="true">https://jsdev.space/howto/strategy-pattern-javascript/</guid><description>Learn how to replace complex if-else chains with the Strategy Pattern in JavaScript using configurable validation, state mapping, and environment-specific logic.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;How to Use the Strategy Pattern in JavaScript&lt;/h2&gt;
&lt;p&gt;Every frontend developer has written a function that started with one &lt;code&gt;if&lt;/code&gt; statement and slowly turned into twenty.&lt;/p&gt;
&lt;p&gt;It usually begins with something harmless.&lt;/p&gt;
&lt;p&gt;You need to validate an email.&lt;/p&gt;
&lt;p&gt;Then someone asks for phone number validation.&lt;/p&gt;
&lt;p&gt;Next comes passwords, usernames, ZIP codes, country-specific rules, feature flags, and suddenly one function contains the business logic for half the application.&lt;/p&gt;
&lt;p&gt;The code still works, but every new requirement makes it harder to read, test, and maintain.&lt;/p&gt;
&lt;p&gt;The Strategy Pattern offers a much cleaner solution.&lt;/p&gt;
&lt;p&gt;Instead of expanding one giant conditional statement, you split each behavior into an independent strategy and choose the correct one at runtime.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Problem with Large if-Else Chains&lt;/h2&gt;
&lt;p&gt;Imagine you&apos;re building a user registration form.&lt;/p&gt;
&lt;p&gt;A common implementation looks something like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function validate(value, type) {
  if (type === &quot;email&quot;) {
    return /\S+@\S+\.\S+/.test(value)
      ? &quot;&quot;
      : &quot;Invalid email address&quot;;
  }

  if (type === &quot;phone&quot;) {
    return /^\+?[1-9]\d{7,14}$/.test(value)
      ? &quot;&quot;
      : &quot;Invalid phone number&quot;;
  }

  if (type === &quot;password&quot;) {
    return value.length &amp;gt;= 8
      ? &quot;&quot;
      : &quot;Password is too short&quot;;
  }

  return &quot;&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation works.&lt;/p&gt;
&lt;p&gt;The problem is scalability.&lt;/p&gt;
&lt;p&gt;Every new validation rule requires modifying the same function.&lt;/p&gt;
&lt;p&gt;Soon dozens of unrelated conditions become tightly coupled, making even small changes risky.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What Is the Strategy Pattern?&lt;/h2&gt;
&lt;p&gt;The Strategy Pattern encapsulates multiple algorithms behind a common interface.&lt;/p&gt;
&lt;p&gt;Instead of asking a function to decide &lt;em&gt;how&lt;/em&gt; something should happen, you provide the behavior you want to execute.&lt;/p&gt;
&lt;p&gt;Rather than writing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if this...
else if that...
else...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Select strategy
↓

Execute strategy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each strategy becomes a small, reusable function with a single responsibility.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Refactoring Validation&lt;/h2&gt;
&lt;p&gt;Instead of putting every rule inside one function, separate them into individual validators.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const validators = {
  required: value =&amp;gt;
    value.trim()
      ? &quot;&quot;
      : &quot;This field is required&quot;,

  email: value =&amp;gt;
    /^\S+@\S+\.\S+$/.test(value)
      ? &quot;&quot;
      : &quot;Invalid email address&quot;,

  phone: value =&amp;gt;
    /^\+?[1-9]\d{7,14}$/.test(value)
      ? &quot;&quot;
      : &quot;Invalid phone number&quot;,

  minLength: length =&amp;gt;
    value =&amp;gt;
      value.length &amp;gt;= length
        ? &quot;&quot;
        : `Minimum ${length} characters`,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the validation engine becomes extremely small.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function validate(value, rules) {

  for (const rule of rules) {

    const strategy =
      typeof rule === &quot;function&quot;
        ? rule
        : validators[rule];

    const error = strategy(value);

    if (error) {
      return error;
    }
  }

  return &quot;&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using it is straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;validate(
  &quot;john@example.com&quot;,
  [&quot;required&quot;, &quot;email&quot;]
);

validate(
  &quot;abc&quot;,
  [
    &quot;required&quot;,
    validators.minLength(8),
  ]
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice what changed.&lt;/p&gt;
&lt;p&gt;Adding a new validation rule never requires touching the validation engine.&lt;/p&gt;
&lt;p&gt;You simply register another strategy.&lt;/p&gt;
&lt;p&gt;This follows one of the core principles of good software design:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open for extension, closed for modification.&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React Integration&lt;/h2&gt;
&lt;p&gt;This approach works naturally with modern React form libraries.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Form.Item
  name=&quot;email&quot;
  rules={[
    {
      validator: (_, value) =&amp;gt;
        validators.email(value),
    },
  ]}
&amp;gt;
  &amp;lt;Input /&amp;gt;
&amp;lt;/Form.Item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The form doesn&apos;t care how email validation works.&lt;/p&gt;
&lt;p&gt;It simply executes the selected strategy.&lt;/p&gt;
&lt;p&gt;The same validators can also be reused in APIs, custom hooks, or server-side validation.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Strategy Pattern for Status Mapping&lt;/h2&gt;
&lt;p&gt;Validation isn&apos;t the only place where developers overuse conditional logic.&lt;/p&gt;
&lt;p&gt;Status mapping is another common example.&lt;/p&gt;
&lt;p&gt;Many applications receive numeric status codes from an API.&lt;/p&gt;
&lt;p&gt;Instead of writing multiple conditional statements throughout the UI, create a configuration object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ORDER_STATUS = {

  pending: {
    label: &quot;Pending&quot;,
    color: &quot;orange&quot;,
  },

  paid: {
    label: &quot;Paid&quot;,
    color: &quot;green&quot;,
  },

  cancelled: {
    label: &quot;Cancelled&quot;,
    color: &quot;gray&quot;,
  },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now rendering becomes much simpler.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const status =
  ORDER_STATUS[order.status];

&amp;lt;Tag color={status.color}&amp;gt;
  {status.label}
&amp;lt;/Tag&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding a new status becomes a single-line change.&lt;/p&gt;
&lt;p&gt;No rendering logic needs to be modified.&lt;/p&gt;
&lt;p&gt;This pattern also works well for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user roles&lt;/li&gt;
&lt;li&gt;notification types&lt;/li&gt;
&lt;li&gt;feature flags&lt;/li&gt;
&lt;li&gt;payment states&lt;/li&gt;
&lt;li&gt;API response codes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Environment-Specific Behavior&lt;/h2&gt;
&lt;p&gt;Different environments often require completely different implementations.&lt;/p&gt;
&lt;p&gt;During development you may want verbose console logging.&lt;/p&gt;
&lt;p&gt;Production applications usually send errors to monitoring services instead.&lt;/p&gt;
&lt;p&gt;Instead of adding environment checks everywhere, provide different strategies.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// devLogger.ts

export const logger = {
  error: console.error,
  warn: console.warn,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// productionLogger.ts

export const logger = {
  error(message, extra) {
    Sentry.captureMessage(
      message,
      { extra }
    );
  },

  warn() {},
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then load the appropriate implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const logger = await import(
  `./loggers/${import.meta.env.MODE}.ts`
);

logger.error(&quot;Something failed&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modern bundlers can tree-shake unused implementations, keeping production bundles smaller.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Other Great Use Cases&lt;/h2&gt;
&lt;p&gt;Once you start looking for them, Strategy Pattern opportunities appear everywhere.&lt;/p&gt;
&lt;p&gt;Formatting values:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Date formatter
Currency formatter
File size formatter
Percentage formatter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Authentication:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Password login
OAuth
Magic links
Passkeys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Payments:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Stripe
PayPal
Square
Adyen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Search algorithms:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Exact match
Fuzzy search
Full-text search
Semantic search
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of branching through multiple implementations, choose the appropriate strategy.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Benefits&lt;/h2&gt;
&lt;p&gt;Using the Strategy Pattern provides several advantages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smaller functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Each strategy has one responsibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Better testing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Strategies can be tested independently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Easy extension&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adding new behavior doesn&apos;t require modifying existing logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reusable code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The same strategies can be shared across multiple features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cleaner architecture&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Business logic becomes separated from decision-making logic.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;When Not to Use It&lt;/h2&gt;
&lt;p&gt;Not every &lt;code&gt;if&lt;/code&gt; statement should become a strategy.&lt;/p&gt;
&lt;p&gt;If you only have two simple conditions that are unlikely to grow, introducing extra abstraction may make the code harder to understand.&lt;/p&gt;
&lt;p&gt;The Strategy Pattern shines when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;new behaviors are added regularly&lt;/li&gt;
&lt;li&gt;multiple implementations share the same interface&lt;/li&gt;
&lt;li&gt;business rules change frequently&lt;/li&gt;
&lt;li&gt;different environments require different behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise, a simple conditional may be perfectly acceptable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;The Strategy Pattern is one of the most practical design patterns in frontend develop…</content:encoded></item><item><title>10 Claude Code Settings That Made Me a Faster Software Architect</title><link>https://jsdev.space/claude-code-settings-software-architect/</link><guid isPermaLink="true">https://jsdev.space/claude-code-settings-software-architect/</guid><description>These ten configuration tweaks dramatically improved accuracy, reduced interruptions, and saved hours of engineering time every month.</description><pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Like most developers, I started by accepting the defaults. They worked well enough, and Claude quickly became part of my daily workflow. But over time I noticed a pattern: the mistakes weren&apos;t random. They repeated themselves.&lt;/p&gt;
&lt;p&gt;The agent would occasionally forget project conventions, lose important context halfway through a long session, or confidently refactor code that didn&apos;t need changing. None of these issues were catastrophic, but together they added up to hours of unnecessary review, corrections, and backtracking every month.&lt;/p&gt;
&lt;p&gt;Instead of blaming the model, I started treating Claude Code like any other engineering tool: something that benefits from configuration.&lt;/p&gt;
&lt;p&gt;After months of experimentation, I ended up with ten changes that noticeably improved both speed and reliability. Some came from the documentation, while others only emerged after making plenty of mistakes in real projects.&lt;/p&gt;
&lt;p&gt;This isn&apos;t a beginner&apos;s guide to Claude Code.&lt;/p&gt;
&lt;p&gt;It&apos;s a collection of workflows, configuration patterns, and practical guardrails for developers who already use the tool and want to get more consistent results.&lt;/p&gt;
&lt;p&gt;Each section includes configuration examples you can copy directly into your own project.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Keep &lt;code&gt;CLAUDE.md&lt;/code&gt; Small&lt;/h2&gt;
&lt;p&gt;One of the biggest mistakes I made during my first few months was turning &lt;code&gt;CLAUDE.md&lt;/code&gt; into an encyclopedia.&lt;/p&gt;
&lt;p&gt;At one point the file had grown to nearly &lt;strong&gt;40,000 characters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It contained architecture decisions, coding conventions, testing rules, domain knowledge, onboarding notes, deployment instructions, prompt templates, API contracts, and even project history.&lt;/p&gt;
&lt;p&gt;My assumption seemed reasonable:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;More context should produce better answers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It didn&apos;t.&lt;/p&gt;
&lt;h3&gt;The Attention Problem&lt;/h3&gt;
&lt;p&gt;Large language models don&apos;t treat every part of a long document equally.&lt;/p&gt;
&lt;p&gt;As the file grew, Claude became increasingly likely to overlook information buried in the middle. Important architectural rules that had worked perfectly before suddenly started disappearing from its reasoning.&lt;/p&gt;
&lt;p&gt;This wasn&apos;t subtle.&lt;/p&gt;
&lt;p&gt;I actually tested it by asking Claude to repeat a rule located around line 300 of my own &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It couldn&apos;t find it.&lt;/p&gt;
&lt;p&gt;After repeating the experiment several times, the pattern became obvious: the longer the document became, the less reliable its middle sections were during extended conversations.&lt;/p&gt;
&lt;h3&gt;My Rule: 8,000 Characters Maximum&lt;/h3&gt;
&lt;p&gt;Today I treat &lt;code&gt;CLAUDE.md&lt;/code&gt; as permanent session context.&lt;/p&gt;
&lt;p&gt;Only information that should appear in &lt;strong&gt;every single conversation&lt;/strong&gt; belongs there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;technology stack&lt;/li&gt;
&lt;li&gt;coding principles&lt;/li&gt;
&lt;li&gt;architectural constraints&lt;/li&gt;
&lt;li&gt;project-wide rules&lt;/li&gt;
&lt;li&gt;links to additional documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything else lives elsewhere.&lt;/p&gt;
&lt;p&gt;My project now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.claude/
├── CLAUDE.md              # Always loaded (&amp;lt;8 KB)
├── skills/
│   ├── nestjs.md
│   ├── migrations.md
│   ├── review.md
│   └── testing.md
└── specs/
    ├── domain-model.md
    ├── api-contracts.md
    └── infrastructure.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of trying to keep every possible detail loaded all the time, I organize information into focused documents that Claude only reads when they&apos;re actually relevant.&lt;/p&gt;
&lt;p&gt;Think of it as lazy loading for project knowledge.&lt;/p&gt;
&lt;h3&gt;The Results&lt;/h3&gt;
&lt;p&gt;After restructuring everything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; shrank from roughly &lt;strong&gt;40K&lt;/strong&gt; to &lt;strong&gt;6K&lt;/strong&gt; characters.&lt;/li&gt;
&lt;li&gt;Session initialization became noticeably cheaper.&lt;/li&gt;
&lt;li&gt;Token usage dropped by roughly &lt;strong&gt;35%&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Claude stopped &quot;forgetting&quot; rules buried in the middle of giant documents.&lt;/li&gt;
&lt;li&gt;Responses became more consistent during long development sessions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This single change produced a bigger improvement than any prompt engineering trick I experimented with.&lt;/p&gt;
&lt;p&gt;Sometimes better AI results don&apos;t come from writing better prompts.&lt;/p&gt;
&lt;p&gt;They come from giving the model less—but better organized—context.&lt;/p&gt;
&lt;h2&gt;2. Three &lt;code&gt;settings.json&lt;/code&gt; Tweaks That Changed My Workflow&lt;/h2&gt;
&lt;p&gt;Most developers never open &lt;code&gt;.claude/settings.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The default configuration is good enough to get started, so it&apos;s easy to ignore. But after a month of daily usage, I realized that a few small changes dramatically improved the experience.&lt;/p&gt;
&lt;p&gt;These are the three settings that had the biggest impact.&lt;/p&gt;
&lt;h3&gt;Allow Safe Commands Automatically&lt;/h3&gt;
&lt;p&gt;The first change was removing unnecessary confirmation prompts.&lt;/p&gt;
&lt;p&gt;Claude spends much of its time reading files, checking Git history, running tests, or searching through the project. None of these operations are destructive, yet by default they often require confirmation.&lt;/p&gt;
&lt;p&gt;I allow read-only operations and verification commands to run automatically while keeping anything destructive behind manual approval.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;permissions&quot;: {
    &quot;allow&quot;: [
      &quot;Bash(git log:*)&quot;,
      &quot;Bash(git diff:*)&quot;,
      &quot;Bash(git status:*)&quot;,
      &quot;Bash(git show:*)&quot;,
      &quot;Bash(npm test:*)&quot;,
      &quot;Bash(npm run lint:*)&quot;,
      &quot;Bash(npm run typecheck:*)&quot;,
      &quot;Bash(cat:*)&quot;,
      &quot;Bash(grep:*)&quot;,
      &quot;Bash(find:*)&quot;
    ],
    &quot;deny&quot;: [
      &quot;Bash(git push:*)&quot;,
      &quot;Bash(git reset --hard:*)&quot;,
      &quot;Bash(rm -rf:*)&quot;
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference was immediate.&lt;/p&gt;
&lt;p&gt;Instead of approving fifteen or twenty harmless operations during a session, I now approve only the actions that could actually change something important.&lt;/p&gt;
&lt;p&gt;Development feels much more like pair programming than remote controlling an assistant.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Log Every Session Automatically&lt;/h3&gt;
&lt;p&gt;The second improvement was surprisingly simple.&lt;/p&gt;
&lt;p&gt;I added a Stop hook that records every completed Claude session.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [{
      &quot;matcher&quot;: &quot;&quot;,
      &quot;hooks&quot;: [{
        &quot;type&quot;: &quot;command&quot;,
        &quot;command&quot;: &quot;echo \&quot;$(date &apos;+%Y-%m-%d %H:%M&apos;) session ended\&quot; &amp;gt;&amp;gt; ~/.claude/log.txt&quot;
      }]
    }]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first I expected this to be little more than curiosity.&lt;/p&gt;
&lt;p&gt;Instead, it gave me real data.&lt;/p&gt;
&lt;p&gt;I discovered I was spending more than two hours every day working with Claude Code across four or five separate sessions. Before measuring it, I would have guessed less than half that amount.&lt;/p&gt;
&lt;p&gt;You can&apos;t optimize what you never measure.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Block Obviously Dangerous Commands&lt;/h3&gt;
&lt;p&gt;Finally, I explicitly deny commands that should never execute accidentally.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;permissions&quot;: {
    &quot;deny&quot;: [
      &quot;Bash(drop table:*)&quot;,
      &quot;Bash(truncate:*)&quot;,
      &quot;Bash(delete from * where 1:*)&quot;
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These rules don&apos;t replace good engineering practices.&lt;/p&gt;
&lt;p&gt;They simply provide another layer of protection against expensive mistakes.&lt;/p&gt;
&lt;p&gt;The entire configuration lives inside the repository, so every developer on the team benefits from the same safety rails instead of relying on individual habits.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Don&apos;t Enable &lt;code&gt;acceptEdits&lt;/code&gt; Everywhere&lt;/h2&gt;
&lt;p&gt;One of Claude Code&apos;s most tempting features is &lt;code&gt;acceptEdits&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It removes confirmation dialogs before file modifications, making the agent feel dramatically faster.&lt;/p&gt;
&lt;p&gt;The first time you enable it, you&apos;ll probably wonder why it isn&apos;t always on.&lt;/p&gt;
&lt;p&gt;After six months of using it, my answer is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Because it shouldn&apos;t be.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Where It Works Well&lt;/h3&gt;
&lt;p&gt;I enable &lt;code&gt;acceptEdits&lt;/code&gt; for tasks that are predictable and easy to verify automatically.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;migrating JavaScript to TypeScript&lt;/li&gt;
&lt;li&gt;replacing &lt;code&gt;any&lt;/code&gt; types&lt;/li&gt;
&lt;li&gt;renaming files or symbols&lt;/li&gt;
&lt;li&gt;updating imports&lt;/li&gt;
&lt;li&gt;formatting code&lt;/li&gt;
&lt;li&gt;ge…</content:encoded></item><item><title>Friday Links #40 — JavaScript News, New Libraries &amp; AI Tools</title><link>https://jsdev.space/friday/friday-40/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-40/</guid><description>The latest JavaScript ecosystem news, React updates, AI developer tools, open source releases, TypeScript, Node.js, CSS, and everything worth reading this week.</description><pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-40.png&quot; alt=&quot;Friday Links #40&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Welcome to Friday Links #40.&lt;/p&gt;
&lt;p&gt;Another busy week brought new JavaScript releases, React improvements, AI-powered developer tools, open-source projects, performance techniques, and plenty of interesting discussions across the web.&lt;/p&gt;
&lt;p&gt;As always, we&apos;ve filtered out the noise and collected only the links that are actually worth your time. Whether you&apos;re building React applications, working with Node.js, experimenting with AI coding assistants, or simply trying to stay current with the modern web ecosystem, this week&apos;s roundup has something useful for you.&lt;/p&gt;
&lt;p&gt;Grab a coffee and enjoy the reading.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.xda-developers.com/a-former-microsoft-engineer-has-rebuilt-notepad-and-its-25kb-in-size-and-has-zero-ai-bloat/&quot;&gt;A former Microsoft engineer has rebuilt Notepad, and it&apos;s 2.5KB in size and has zero AI bloat&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jakearchibald.com/2026/goldilocks-select-height/&quot;&gt;The Goldilocks customizable select height&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://neciudan.dev/hydration-and-rendering-strategies&quot;&gt;Different hydration and rendering strategies&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/usermedia-html-element&quot;&gt;Introducing the &lt;code&gt;&amp;lt;usermedia&amp;gt;&lt;/code&gt; HTML element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/css-states-and-javascript-events/&quot;&gt;The Shifting Line Between CSS States and JavaScript Events&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://polypane.app/blog/experimenting-with-random-in-css/&quot;&gt;Experimenting with random() in CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cassidoo.co/post/radial-mask-text-css/&quot;&gt;An animated radial gradient mask over text in CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://microlink.io/blog/webgl-without-a-gpu&quot;&gt;WebGL without a GPU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://quirksmode.org/quirksblog/2026/0527-gridalgo.html&quot;&gt;Grid algorithm adventures&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.blog/changelog/2026-06-25-actions-steps-can-now-be-run-in-parallel/&quot;&gt;Actions steps can now be run in parallel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.googleblog.com/build-agentic-full-stack-apps-with-genkit/&quot;&gt;Announcing Genkit Agents: A full-stack foundation for conversational AI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/blogs/2026/06/26/iterating-faster-with-ts-7&quot;&gt;VSCode - Iterating faster with TypeScript 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jstools.space/blog/read-large-json-files/&quot;&gt;How to Read Large JSON Files Without Losing Your Mind&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://localhost:4322/json-formatter/&quot;&gt;JSON Formatter&lt;/a&gt; - A simple and fast JSON formatter and validator for developers. It can format, validate, and beautify JSON data in a user-friendly interface.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jstools-space.gitbook.io/json-handbook&quot;&gt;JSON Handbook&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/raiyanyahya/recall&quot;&gt;Recall&lt;/a&gt; - fully-local project memory for Claude Code&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/stablyai/orca&quot;&gt;Orca&lt;/a&gt; - is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop and mobile.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://infographic.antv.vision/&quot;&gt;Infographic&lt;/a&gt; - An Infographic Generation and Rendering Framework, bring words to life with AI!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/breschio/drawbridge&quot;&gt;Drawbridge&lt;/a&gt; - Design editor for Claude Code and Cursor. &quot;Figma Comments&quot; for the browser, are sent directly to Claude Code and Cursor as prompts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jarrodwatts/claude-hud&quot;&gt;Claude HUD&lt;/a&gt; - A Claude Code plugin that shows what&apos;s happening — context usage, active tools, running agents, and todo progress. Always visible below your input.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EpicGames/lore&quot;&gt;Lore&lt;/a&gt; - Lore is a next-generation, open source version control system&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/daloyjs/daloy&quot;&gt;DaloyJS&lt;/a&gt; - The secure TypeScript API framework that runs anywhere&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/pdfme/pdfme&quot;&gt;pdfme&lt;/a&gt; - Open-source PDF generation library built with TypeScript and React. Features a WYSIWYG template designer, PDF viewer, and powerful generation capabilities.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ddoemonn/hiraki&quot;&gt;hiraki&lt;/a&gt; - A zero-dependency React drawer component. All 4 directions, velocity-aware gestures, snap points, 6 variants, and a pure CSS animation system without Radix, Framer Motion, or any external runtime dependencies.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/thoughtfulllc/gridland&quot;&gt;Gridland&lt;/a&gt; - Terminal apps that run anywhere - even the browser!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rari-build/rari&quot;&gt;rari&lt;/a&gt; - High-performance React Server Components framework powered by a Rust runtime, delivering 18.1x faster response times (0.12ms vs 2.17ms) and 67.4x higher throughput (97,826 vs 1,452 req/sec) than Next.js with zero-config setup.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/marcoaapfortes/Mantic.sh&quot;&gt;Mantic.sh&lt;/a&gt; - A structural code search engine for Al agents.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zhw2590582/ArtPlayer&quot;&gt;ArtPlayer&lt;/a&gt; - ArtPlayer.js is a modern and full featured HTML5 video player&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/educastellano/qr-code&quot;&gt;&lt;code&gt;&amp;lt;qr-code&amp;gt;&lt;/code&gt;&lt;/a&gt; - Web Component for generating QR Codes&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jsdom/whatwg-url&quot;&gt;whatwg-url&lt;/a&gt; - An implementation of the WHATWG URL Standard in JavaScript&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build/blog/astro-7/&quot;&gt;Astro 7.0 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Astro 7 is the biggest release since Astro 5, with a strong focus on build performance and AI-powered workflows. The framework now ships with Vite 8, a brand-new Rust-based .astro compiler, Rust-powered Markdown/MDX processing, queued rendering, and stable route caching. The Astro team reports 15–61% faster builds, with some large documentation sites building more than twice as fast. Astro 7 also introduces Advanced Routing, astro dev --background for AI coding agents, and structured JSON logging.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.angular.dev/announcing-angular-v22-c52bb83a4664&quot;&gt;Announcing Angular v22&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/commandline/intelligent-terminal-0-1-1-is-here-bash-support-new-slash-commands-and-customization/&quot;&gt;Intelligent Terminal 0.1.1 is here: bash support, new slash commands, and more customization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vite.dev/blog/announcing-vite8-1&quot;&gt;Vite 8.1 is out!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/en/blog/release/v26.4.0&quot;&gt;Node.js 26.4.0 (Current)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rspack.rs/blog/announcing-2-1&quot;&gt;Announcing Rspack 2.1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/react-hook-form/react-hook-form/releases/tag/v7.80.0&quot;&gt;React Hook Form v7.80&lt;/a&gt;, &lt;a href=&quot;https://reactnative.dev/blog/2026/06/11/react-native-0.86&quot;&gt;React Native v0.86&lt;/a&gt;, &lt;a href=&quot;https://motion.dev/docs/arc&quot;&gt;Motion v12.40&lt;/a&gt;, &lt;a href=&quot;https://www.electronjs.org/blog/electron-43-0&quot;&gt;Electron 43&lt;/a&gt;, &lt;a href=&quot;https://nextjs.org/blog/next-16-3-instant-navigations&quot;&gt;Next.js 16.3: Instant Navigations&lt;/a&gt;, &lt;a href=&quot;https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v810&quot;&gt;React-Router 8.1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rspack.rs/blog/announcing-2-1&quot;&gt;Announcing Rspack 2.1&lt;/a&gt;, &lt;a href=&quot;https://github.com/47ng/nuqs/releases/tag/v2.9.0&quot;&gt;nuqs 2.9&lt;/a&gt;, &lt;a href=&quot;https://github.com/redwoodjs/sdk/releases/tag/v1.5.0&quot;&gt;RedwoodSDK 1.5&lt;/a&gt;, &lt;a href=&quot;https://prettier.io/blog/2026/06/27/3.9.0&quot;&gt;Prettier 3.9&lt;/a&gt;, &lt;a href=&quot;https://deno.com/blog/v2.9#deno-desktop&quot;&gt;Deno 2.9&lt;/a&gt;, &lt;a href=&quot;https://webpack.js.org/blog/2026-06-25-webpack-5-108/&quot;&gt;Webpack 5.108&lt;/a&gt;, &lt;a href=&quot;https://eslint.org/blog/2026/06/eslint-v10.6.0-released/&quot;&gt;ESLint v10.6.0 released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.blog/open-source/git/highlights-from-git-2-55/&quot;&gt;Highlights from Git 2.55&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📺 Videos&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Q6NpiIp-6WM&quot;&gt;NestJS Full Course for Beginners in 2026 | Build a Production-Ready API&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=8FSLsVAJj2w&quot;&gt;Modern UI…</content:encoded></item><item><title>Docker Explained: A Practical Guide to Containers for Developers</title><link>https://jsdev.space/docker-fundamentals-guide/</link><guid isPermaLink="true">https://jsdev.space/docker-fundamentals-guide/</guid><description>Learn Docker from the ground up. Understand containers, images, isolation, Dockerfiles, best practices, and real-world workflows with practical examples.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Why Docker Exists&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Docker has become one of those technologies that almost every developer eventually encounters. Whether you&apos;re building a small side project, deploying a production API, or preparing for a DevOps interview, Docker is everywhere.&lt;/p&gt;
&lt;p&gt;Yet many developers use it every day without fully understanding &lt;em&gt;why&lt;/em&gt; it exists.&lt;/p&gt;
&lt;p&gt;You have probably seen commands like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker build
docker run
docker compose up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and perhaps even copied a Dockerfile from another repository without thinking much about what happens behind the scenes.&lt;/p&gt;
&lt;p&gt;That works—for a while.&lt;/p&gt;
&lt;p&gt;Sooner or later, however, you&apos;ll run into questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why does my application work on my machine but fail in production?&lt;/li&gt;
&lt;li&gt;Why is this Docker image 3 GB?&lt;/li&gt;
&lt;li&gt;Why does every build take several minutes?&lt;/li&gt;
&lt;li&gt;Why did updating a base image suddenly break my application?&lt;/li&gt;
&lt;li&gt;What exactly is the difference between an image and a container?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide answers those questions from first principles.&lt;/p&gt;
&lt;p&gt;Rather than memorizing commands, you&apos;ll learn how Docker actually works, why its design solves real engineering problems, and how experienced developers structure their containerized applications.&lt;/p&gt;
&lt;p&gt;By the end of this guide you&apos;ll understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;why containers became the industry standard&lt;/li&gt;
&lt;li&gt;what Docker actually does&lt;/li&gt;
&lt;li&gt;how images are built&lt;/li&gt;
&lt;li&gt;how containers run&lt;/li&gt;
&lt;li&gt;how isolation works&lt;/li&gt;
&lt;li&gt;how to write cleaner Dockerfiles&lt;/li&gt;
&lt;li&gt;how to avoid common beginner mistakes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Docker Was Created&lt;/h2&gt;
&lt;p&gt;Before Docker became mainstream, deploying software was surprisingly fragile.&lt;/p&gt;
&lt;p&gt;Imagine a simple Python web application.&lt;/p&gt;
&lt;p&gt;It depends on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.11&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;several third-party libraries&lt;/li&gt;
&lt;li&gt;operating system packages&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;configuration files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything works perfectly on your laptop.&lt;/p&gt;
&lt;p&gt;Then you send the project to a teammate.&lt;/p&gt;
&lt;p&gt;Five minutes later they message you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;It doesn&apos;t work.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nothing changed in your code.&lt;/p&gt;
&lt;p&gt;Yet somehow everything is broken.&lt;/p&gt;
&lt;p&gt;This situation became so common that developers jokingly called it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&quot;Works on my machine.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The problem wasn&apos;t the application.&lt;/p&gt;
&lt;p&gt;The problem was the environment.&lt;/p&gt;
&lt;p&gt;Different computers had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;different operating systems&lt;/li&gt;
&lt;li&gt;different package versions&lt;/li&gt;
&lt;li&gt;different runtime versions&lt;/li&gt;
&lt;li&gt;different system libraries&lt;/li&gt;
&lt;li&gt;different configurations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even reinstalling your own project six months later could produce completely different results.&lt;/p&gt;
&lt;p&gt;Docker was designed to eliminate this entire category of problems.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;One Application, One Environment&lt;/h2&gt;
&lt;p&gt;Instead of installing everything directly onto the operating system, Docker packages an application together with everything it needs to run.&lt;/p&gt;
&lt;p&gt;That package is called an &lt;strong&gt;image&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;An image contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your application&lt;/li&gt;
&lt;li&gt;the runtime&lt;/li&gt;
&lt;li&gt;installed dependencies&lt;/li&gt;
&lt;li&gt;configuration&lt;/li&gt;
&lt;li&gt;operating system libraries&lt;/li&gt;
&lt;li&gt;startup commands&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything is bundled into a predictable environment.&lt;/p&gt;
&lt;p&gt;If the image contains Python 3.11, Docker will always use Python 3.11.&lt;/p&gt;
&lt;p&gt;If it contains Node.js 22, every container created from that image will use Node.js 22.&lt;/p&gt;
&lt;p&gt;It doesn&apos;t matter whether the image runs on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your laptop&lt;/li&gt;
&lt;li&gt;a teammate&apos;s computer&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;DigitalOcean&lt;/li&gt;
&lt;li&gt;a Kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The environment remains identical.&lt;/p&gt;
&lt;p&gt;That&apos;s Docker&apos;s biggest promise:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Build once. Run anywhere.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Stability Through Reproducibility&lt;/h2&gt;
&lt;p&gt;Modern software changes constantly.&lt;/p&gt;
&lt;p&gt;Package managers receive updates.&lt;/p&gt;
&lt;p&gt;Operating systems evolve.&lt;/p&gt;
&lt;p&gt;Libraries introduce breaking changes.&lt;/p&gt;
&lt;p&gt;Configuration defaults change.&lt;/p&gt;
&lt;p&gt;Without isolation, even a small system update can unexpectedly break a working application.&lt;/p&gt;
&lt;p&gt;Docker avoids this by freezing the execution environment inside an image.&lt;/p&gt;
&lt;p&gt;Instead of relying on whatever happens to be installed on the host machine, every container starts from the exact same filesystem and dependency versions every time.&lt;/p&gt;
&lt;p&gt;This makes deployments reproducible.&lt;/p&gt;
&lt;p&gt;If version &lt;strong&gt;1.2.0&lt;/strong&gt; of your application worked yesterday, it will continue to work next month—as long as you&apos;re running the same image.&lt;/p&gt;
&lt;p&gt;For teams, this dramatically reduces debugging time because everyone is running the same environment.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Isolation: Keeping Applications Out of Each Other&apos;s Way&lt;/h2&gt;
&lt;p&gt;Stability is only half of Docker&apos;s value.&lt;/p&gt;
&lt;p&gt;The other half is &lt;strong&gt;isolation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Imagine a server hosting several applications.&lt;/p&gt;
&lt;p&gt;One service requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.9&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.12&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A third depends on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL 14&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another expects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL 16&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installing everything directly on one operating system quickly becomes difficult.&lt;/p&gt;
&lt;p&gt;Different versions overwrite one another.&lt;/p&gt;
&lt;p&gt;Configuration files collide.&lt;/p&gt;
&lt;p&gt;Library updates break unrelated services.&lt;/p&gt;
&lt;p&gt;Docker solves this by giving every application its own isolated environment.&lt;/p&gt;
&lt;p&gt;Each container has its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filesystem&lt;/li&gt;
&lt;li&gt;processes&lt;/li&gt;
&lt;li&gt;network interfaces&lt;/li&gt;
&lt;li&gt;installed libraries&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Containers don&apos;t interfere with one another.&lt;/p&gt;
&lt;p&gt;You can run multiple versions of the same runtime side by side without conflicts.&lt;/p&gt;
&lt;p&gt;This isolation makes Docker particularly useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;microservices&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;development environments&lt;/li&gt;
&lt;li&gt;testing multiple software versions&lt;/li&gt;
&lt;li&gt;self-hosted infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Resource Limits&lt;/h2&gt;
&lt;p&gt;Isolation isn&apos;t just about software.&lt;/p&gt;
&lt;p&gt;Docker can also isolate hardware resources.&lt;/p&gt;
&lt;p&gt;A container can be limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a fixed amount of RAM&lt;/li&gt;
&lt;li&gt;a specific number of CPU cores&lt;/li&gt;
&lt;li&gt;storage quotas&lt;/li&gt;
&lt;li&gt;process limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, a logging service can be restricted to 512 MB of memory, while a database receives several gigabytes.&lt;/p&gt;
&lt;p&gt;If one container starts consuming excessive resources, Docker prevents it from affecting the rest of the system.&lt;/p&gt;
&lt;p&gt;Instead of allowing a single runaway application to crash an entire server, each workload stays within its assigned limits.&lt;/p&gt;
&lt;p&gt;This is one of the reasons Docker has become such a common foundation for modern cloud infrastructure.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Developers Love Docker&lt;/h2&gt;
&lt;p&gt;After understanding these ideas, Docker becomes much easier to appreciate.&lt;/p&gt;
&lt;p&gt;Its popularity isn&apos;t about learning new commands.&lt;/p&gt;
&lt;p&gt;It&apos;s about solving real engineering problems.&lt;/p&gt;
&lt;p&gt;Docker gives developers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;predictable environments&lt;/li&gt;
&lt;li&gt;repeatable deployments&lt;/li&gt;
&lt;li&gt;isolated applications&lt;/li&gt;
&lt;li&gt;easier collaboration&lt;/li&gt;
&lt;li&gt;safer experimentation&lt;/li&gt;
&lt;li&gt;simpler onboarding&lt;/li&gt;
&lt;li&gt;cleaner production servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you&apos;ve experienced working in fully reproducible environments, it&apos;s surprisingly difficult to go back to manually configuring applications on every machine.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker Terminology Every Developer Should Know&lt;/h2&gt;
&lt;p&gt;Before writing your first Dockerfile or running your first container, it&apos;s important to understand Docker&apos;s vocabulary. Many beginners struggle not because Docker is difficult, but because its documentation assumes you already know what terms like &lt;em&gt;image&lt;/em&gt;, &lt;em&gt;c…</content:encoded></item><item><title>Modern CSS Features That Replace Old Tricks</title><link>https://jsdev.space/modern-css-features/</link><guid isPermaLink="true">https://jsdev.space/modern-css-features/</guid><description>Discover modern CSS features that replace outdated hacks for modals, fonts, alignment, dark mode, and international layouts with cleaner, more maintainable code.</description><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;CSS has changed more in the last few years than it did during the previous decade.&lt;/p&gt;
&lt;p&gt;Many patterns that developers once considered &quot;best practice&quot; were actually workarounds for missing browser features. We relied on JavaScript to compensate for layout problems, added unnecessary wrappers, and wrote dozens of lines of CSS just to accomplish relatively simple tasks.&lt;/p&gt;
&lt;p&gt;Today, most of those hacks are no longer necessary.&lt;/p&gt;
&lt;p&gt;Modern browsers support a collection of new CSS features that make code shorter, more readable, and easier to maintain. If your stylesheets still contain old patterns copied from projects built five or ten years ago, chances are you can simplify them considerably.&lt;/p&gt;
&lt;p&gt;Let&apos;s look at several CSS features that deserve a permanent place in every modern codebase.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Stop Fixing Layout Shifts with JavaScript&lt;/h2&gt;
&lt;p&gt;One of the oldest UI problems appears whenever a modal dialog opens.&lt;/p&gt;
&lt;p&gt;The browser removes the scrollbar after &lt;code&gt;overflow: hidden&lt;/code&gt; is applied to the page, causing the entire layout to jump horizontally.&lt;/p&gt;
&lt;p&gt;For years developers compensated by manually calculating the scrollbar width.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body.modal-open {
  overflow: hidden;
  margin-right: 14px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Others preferred changing the body&apos;s width instead.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body.modal-open {
  position: fixed;
  overflow: hidden;
  width: calc(100% - 14px);
  height: 100%;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both approaches work.&lt;/p&gt;
&lt;p&gt;Neither is particularly elegant.&lt;/p&gt;
&lt;p&gt;The hardcoded value depends on the operating system, browser, zoom level, and accessibility settings. Sooner or later someone reports that the layout still shifts.&lt;/p&gt;
&lt;p&gt;Modern CSS solves this with a single property.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;html {
  scrollbar-gutter: stable;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s it.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;scrollbar-gutter: stable&lt;/code&gt; is enabled, the browser permanently reserves space for the scrollbar. Opening a modal no longer changes the layout because that space already exists.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;html,
body {
  scrollbar-gutter: stable;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browser determines the correct scrollbar width automatically.&lt;/p&gt;
&lt;p&gt;No calculations.&lt;/p&gt;
&lt;p&gt;No JavaScript.&lt;/p&gt;
&lt;p&gt;No platform-specific fixes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Replace Generic Font Fallbacks with &lt;code&gt;system-ui&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Many projects still define fonts like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
  font-family: &quot;Inter&quot;, sans-serif;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing technically wrong with it.&lt;/p&gt;
&lt;p&gt;The problem appears when the custom font fails to load.&lt;/p&gt;
&lt;p&gt;The browser falls back to an arbitrary sans-serif font that often looks completely different from the rest of the operating system.&lt;/p&gt;
&lt;p&gt;Modern CSS offers a better fallback.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
  font-family: &quot;Inter&quot;, system-ui;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;system-ui&lt;/code&gt; keyword tells the browser to use the platform&apos;s native interface font.&lt;/p&gt;
&lt;p&gt;That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;San Francisco on macOS and iOS&lt;/li&gt;
&lt;li&gt;Segoe UI on Windows&lt;/li&gt;
&lt;li&gt;Roboto on Android&lt;/li&gt;
&lt;li&gt;Ubuntu on Ubuntu Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result feels much more natural because your application immediately adopts the typography users already see throughout their operating system.&lt;/p&gt;
&lt;p&gt;If your custom font loads successfully, nothing changes.&lt;/p&gt;
&lt;p&gt;If it doesn&apos;t, the fallback still looks intentional.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Center Elements Without Flexbox&lt;/h2&gt;
&lt;p&gt;Ask most developers how they center an element and the answer is usually one of these.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.container {
  display: flex;
  justify-content: center;
  align-items: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.container {
  display: grid;
  place-items: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both are perfectly valid.&lt;/p&gt;
&lt;p&gt;The question is whether you actually need Flexbox or Grid.&lt;/p&gt;
&lt;p&gt;If the parent only contains one child, modern CSS provides a surprisingly clean alternative.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.container {
  min-height: 300px;
  align-content: center;
}

.card {
  width: 240px;
  margin-inline: auto;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the responsibilities are divided naturally.&lt;/p&gt;
&lt;p&gt;The parent handles vertical alignment.&lt;/p&gt;
&lt;p&gt;The child centers itself horizontally.&lt;/p&gt;
&lt;p&gt;There is no need to introduce Flexbox solely for centering.&lt;/p&gt;
&lt;p&gt;This approach reduces unnecessary layout contexts and keeps the CSS easier to reason about.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Simplify Dark Mode with &lt;code&gt;light-dark()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Dark mode used to require media queries everywhere.&lt;/p&gt;
&lt;p&gt;A typical implementation looked like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  --background: white;
  --text: black;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #1b1b1b;
    --text: white;
  }
}

body {
  background: var(--background);
  color: var(--text);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing fundamentally wrong with this pattern.&lt;/p&gt;
&lt;p&gt;It simply becomes repetitive as projects grow.&lt;/p&gt;
&lt;p&gt;Modern CSS introduces &lt;code&gt;light-dark()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  color-scheme: light dark;

  --background: light-dark(#ffffff, #1b1b1b);
  --text: light-dark(#222222, #f4f4f4);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest of the stylesheet remains unchanged.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
  background: var(--background);
  color: var(--text);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browser automatically selects the appropriate value based on the user&apos;s preferred color scheme.&lt;/p&gt;
&lt;p&gt;This significantly reduces duplicated variables and media queries.&lt;/p&gt;
&lt;p&gt;The only requirement is enabling support with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  color-scheme: light dark;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For projects supporting both light and dark themes, &lt;code&gt;light-dark()&lt;/code&gt; quickly becomes one of the most useful additions to modern CSS.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Stop Using &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;right&lt;/code&gt; for Text Alignment&lt;/h2&gt;
&lt;p&gt;Most websites are built for left-to-right languages.&lt;/p&gt;
&lt;p&gt;As a result, developers often write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.article {
  text-align: left;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.article {
  text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These values work until the application needs to support Arabic, Hebrew, or another right-to-left language.&lt;/p&gt;
&lt;p&gt;Modern CSS solves this elegantly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.article {
  text-align: start;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.article {
  text-align: end;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browser automatically adapts.&lt;/p&gt;
&lt;p&gt;For English:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;start → left
end   → right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Arabic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;start → right
end   → left
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No additional CSS is required.&lt;/p&gt;
&lt;p&gt;Logical properties make internationalization almost effortless and eliminate another class of layout bugs.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why These Features Matter&lt;/h2&gt;
&lt;p&gt;None of these properties are revolutionary on their own.&lt;/p&gt;
&lt;p&gt;Their real value comes from what they replace.&lt;/p&gt;
&lt;p&gt;Instead of JavaScript hacks, manual calculations, duplicated media queries, and unnecessary layout containers, developers can rely on native browser behavior.&lt;/p&gt;
&lt;p&gt;That leads to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Less code&lt;/li&gt;
&lt;li&gt;Fewer bugs&lt;/li&gt;
&lt;li&gt;Better accessibility&lt;/li&gt;
&lt;li&gt;Easier maintenance&lt;/li&gt;
&lt;li&gt;More predictable layouts&lt;/li&gt;
&lt;li&gt;Better internationalization support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Modern CSS is no longer just about writing styles.&lt;/p&gt;
&lt;p&gt;It increasingly provides high-level layout and UI primitives that previously required frameworks or custom utilities.&lt;/p&gt;
&lt;p&gt;The more of these features you adopt, the smaller your stylesheets become.&lt;/p&gt;
&lt;p&gt;Ironically, writing modern CSS often means writing much less CSS.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;CSS has matured into an incredibly capable language.&lt;/p&gt;
&lt;p&gt;Properties such as &lt;code&gt;scrollbar-gutter&lt;/code&gt;, &lt;code&gt;system-ui&lt;/code&gt;, &lt;code&gt;margin-inline&lt;/code&gt;, &lt;code&gt;align-content&lt;/code&gt;, &lt;code&gt;light-dark()&lt;/code&gt;, and logical alignment values eliminate many patterns that developers relied on for years.&lt;/p&gt;
&lt;p&gt;If you&apos;re starting a new project today, there is little reason to carry those old workarounds forward.&lt;/p&gt;
&lt;p&gt;The browser can now solve many of those problems…</content:encoded></item><item><title>Bulletproof React: Building React Apps That Survive Growth</title><link>https://jsdev.space/bulletproof-react-architecture-guide/</link><guid isPermaLink="true">https://jsdev.space/bulletproof-react-architecture-guide/</guid><description>Learn how Bulletproof React uses feature-based architecture, public APIs, TanStack Query, Zustand.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every React developer has experienced this moment.&lt;/p&gt;
&lt;p&gt;You open a project you built six months ago and immediately wonder:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Why is this component here?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Why does this hook depend on three unrelated modules?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Why are API requests scattered across five different directories?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And perhaps the most terrifying question:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Who wrote this code?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then you realize it was you.&lt;/p&gt;
&lt;p&gt;React is one of the most flexible frontend frameworks ever created. It gives developers complete freedom over component design, state management, folder structures, API communication, testing strategies, and architectural decisions.&lt;/p&gt;
&lt;p&gt;That freedom is both React&apos;s greatest strength and its biggest weakness.&lt;/p&gt;
&lt;p&gt;Without clear boundaries, most React projects slowly evolve into collections of loosely connected components, utilities, hooks, and services that become increasingly difficult to understand, maintain, and extend.&lt;/p&gt;
&lt;p&gt;This problem becomes especially painful as teams grow.&lt;/p&gt;
&lt;p&gt;What starts as a clean side project eventually becomes a codebase nobody fully understands.&lt;/p&gt;
&lt;p&gt;That is exactly the problem that &lt;strong&gt;Bulletproof React&lt;/strong&gt; was created to solve.&lt;/p&gt;
&lt;p&gt;The ideas discussed in this article are inspired by Alan Alickovic&apos;s excellent open-source project - &lt;a href=&quot;https://github.com/alan2207/bulletproof-react&quot;&gt;bulletproof-react&lt;/a&gt; - which has become one of the most popular React architecture references on GitHub.&lt;/p&gt;
&lt;p&gt;Rather than providing another boilerplate or starter kit, Bulletproof React offers a set of architectural principles and conventions designed to keep React applications maintainable long after their initial release.&lt;/p&gt;
&lt;p&gt;Let&apos;s explore why it has become one of the most respected React architecture references on GitHub.&lt;/p&gt;
&lt;h2&gt;The Real Problem With React&lt;/h2&gt;
&lt;p&gt;React intentionally avoids enforcing architectural decisions.&lt;/p&gt;
&lt;p&gt;Unlike frameworks that prescribe strict folder structures and application boundaries, React gives developers complete freedom.&lt;/p&gt;
&lt;p&gt;At first, that feels empowering.&lt;/p&gt;
&lt;p&gt;A typical project starts with something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/
  components/
  hooks/
  utils/
  services/
  pages/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything looks perfectly reasonable.&lt;/p&gt;
&lt;p&gt;Until it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;As the application grows, related code becomes scattered throughout the project.&lt;/p&gt;
&lt;p&gt;A profile page may depend on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;components/UserProfile.tsx&lt;/li&gt;
&lt;li&gt;hooks/useUser.ts&lt;/li&gt;
&lt;li&gt;services/userApi.ts&lt;/li&gt;
&lt;li&gt;utils/userHelpers.ts&lt;/li&gt;
&lt;li&gt;types/user.ts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding a single feature requires jumping between multiple unrelated directories.&lt;/p&gt;
&lt;p&gt;The project is technically organized.&lt;/p&gt;
&lt;p&gt;But it isn&apos;t structured around how developers actually think.&lt;/p&gt;
&lt;p&gt;Developers think in features.&lt;/p&gt;
&lt;p&gt;Traditional React projects are usually organized around file types.&lt;/p&gt;
&lt;p&gt;That&apos;s where Bulletproof React starts.&lt;/p&gt;
&lt;h2&gt;The Core Idea: Organize by Features&lt;/h2&gt;
&lt;p&gt;The most important concept in Bulletproof React is feature-based architecture.&lt;/p&gt;
&lt;p&gt;Instead of grouping code by technical category, related functionality lives together.&lt;/p&gt;
&lt;p&gt;A simplified example looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/
  features/
    auth/
    projects/
    comments/
    notifications/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each feature contains everything it needs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auth/
  api/
  components/
  hooks/
  stores/
  types/
  utils/
  index.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Authentication logic stays inside authentication.&lt;/p&gt;
&lt;p&gt;Project management logic stays inside projects.&lt;/p&gt;
&lt;p&gt;Comment functionality stays inside comments.&lt;/p&gt;
&lt;p&gt;Everything related to a feature lives together.&lt;/p&gt;
&lt;p&gt;This creates several immediate advantages.&lt;/p&gt;
&lt;h3&gt;Better Discoverability&lt;/h3&gt;
&lt;p&gt;When a developer needs to modify authentication, they know exactly where to go.&lt;/p&gt;
&lt;p&gt;No more hunting across five unrelated folders.&lt;/p&gt;
&lt;h3&gt;Easier Refactoring&lt;/h3&gt;
&lt;p&gt;Changes remain localized.&lt;/p&gt;
&lt;p&gt;Modifying a feature rarely impacts unrelated parts of the application.&lt;/p&gt;
&lt;h3&gt;Team Scalability&lt;/h3&gt;
&lt;p&gt;Multiple developers can work simultaneously on different features without constantly stepping on each other&apos;s toes.&lt;/p&gt;
&lt;h3&gt;Clear Ownership&lt;/h3&gt;
&lt;p&gt;Features naturally become bounded contexts.&lt;/p&gt;
&lt;p&gt;Developers can reason about them independently.&lt;/p&gt;
&lt;p&gt;This is one of the biggest reasons large engineering teams increasingly prefer feature-driven architectures.&lt;/p&gt;
&lt;h2&gt;Shared Code Still Exists&lt;/h2&gt;
&lt;p&gt;Not everything belongs inside a feature.&lt;/p&gt;
&lt;p&gt;Some code is truly reusable.&lt;/p&gt;
&lt;p&gt;Bulletproof React keeps shared infrastructure at the application level:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/
  components/
    ui/
    layout/

  lib/
  providers/
  routes/
  types/
  utils/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key rule is simple:&lt;/p&gt;
&lt;p&gt;If something is only used by one feature, it belongs inside that feature.&lt;/p&gt;
&lt;p&gt;If it is reused across multiple features, it can be promoted to shared infrastructure.&lt;/p&gt;
&lt;p&gt;Many teams accidentally reverse this rule and end up with gigantic shared component directories that nobody understands.&lt;/p&gt;
&lt;h2&gt;Public APIs: Protecting Your Architecture&lt;/h2&gt;
&lt;p&gt;Feature-based architecture alone is not enough.&lt;/p&gt;
&lt;p&gt;Developers can still bypass boundaries.&lt;/p&gt;
&lt;p&gt;Imagine this import:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { PrivateRoute } from &apos;@/features/auth/components/PrivateRoute&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Technically it works.&lt;/p&gt;
&lt;p&gt;Architecturally it&apos;s dangerous.&lt;/p&gt;
&lt;p&gt;The importing module now depends on an internal implementation detail.&lt;/p&gt;
&lt;p&gt;If the authentication team refactors its internal structure, unrelated parts of the application break.&lt;/p&gt;
&lt;p&gt;Bulletproof React solves this through Public APIs.&lt;/p&gt;
&lt;p&gt;Every feature exposes only approved exports through its root index file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export { useAuth } from &apos;./hooks/useAuth&apos;;
export { AuthProvider } from &apos;./components/AuthProvider&apos;;
export type { User } from &apos;./types&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consumers import only from the feature root:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useAuth } from &apos;@/features/auth&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything else remains private.&lt;/p&gt;
&lt;p&gt;This approach mirrors how libraries and packages expose public interfaces.&lt;/p&gt;
&lt;p&gt;Features become miniature applications with clearly defined contracts.&lt;/p&gt;
&lt;h2&gt;Enforcing Architecture With ESLint&lt;/h2&gt;
&lt;p&gt;Architecture documentation is helpful.&lt;/p&gt;
&lt;p&gt;Automated enforcement is better.&lt;/p&gt;
&lt;p&gt;Bulletproof React strongly encourages using ESLint rules to prevent illegal imports.&lt;/p&gt;
&lt;p&gt;Without enforcement, architectural boundaries eventually become suggestions.&lt;/p&gt;
&lt;p&gt;With enforcement, they become guarantees.&lt;/p&gt;
&lt;p&gt;A properly configured ESLint setup can block imports from internal feature directories and ensure that all communication happens through public APIs.&lt;/p&gt;
&lt;p&gt;This transforms architecture from a convention into a system.&lt;/p&gt;
&lt;p&gt;The result is less architectural drift and fewer accidental dependencies.&lt;/p&gt;
&lt;h2&gt;Rethinking State Management&lt;/h2&gt;
&lt;p&gt;Few topics generate more debate in the React ecosystem than state management.&lt;/p&gt;
&lt;p&gt;Redux.&lt;/p&gt;
&lt;p&gt;Context.&lt;/p&gt;
&lt;p&gt;Zustand.&lt;/p&gt;
&lt;p&gt;MobX.&lt;/p&gt;
&lt;p&gt;Jotai.&lt;/p&gt;
&lt;p&gt;Recoil.&lt;/p&gt;
&lt;p&gt;The list never ends.&lt;/p&gt;
&lt;p&gt;Bulletproof React approaches the problem differently.&lt;/p&gt;
&lt;p&gt;Instead of asking:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;Which state library should we use?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It asks:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;What kind of state is this?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That distinction changes everything.&lt;/p&gt;
&lt;h3&gt;UI State&lt;/h3&gt;
&lt;p&gt;Temporary component state belongs inside components.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [isOpen, setIsOpen] = useState(false);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple.&lt;/p&gt;
&lt;p&gt;Local.&lt;/p&gt;
&lt;p&gt;No global storage required.&lt;/p&gt;
&lt;h3&gt;Application State&lt;/h3&gt;
&lt;p&gt;Things like notifications, themes, and global UI behavior belong in lightweight state stores.&lt;/p&gt;
&lt;p&gt;Libraries like Zustand excel here.&lt;/p&gt;
&lt;h3&gt;Server State&lt;/h3&gt;
&lt;p&gt;This is where many React projects make mistakes.&lt;/p&gt;
&lt;p&gt;Data fetched from an API is not application state.&lt;/p&gt;
&lt;p&gt;It is cached server state.&lt;/p&gt;
&lt;p&gt;Treating server d…</content:encoded></item><item><title>Howto Use Next.js Route Handlers as a Mini Backend</title><link>https://jsdev.space/howto/nextjs-route-handlers-mini-backend/</link><guid isPermaLink="true">https://jsdev.space/howto/nextjs-route-handlers-mini-backend/</guid><description>Learn how Next.js Route Handlers create a clean server boundary for APIs, env variables, validation, and normalized UI data.</description><pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When a Next.js project starts small, calling an external API directly from a page feels like the fastest path.&lt;/p&gt;
&lt;p&gt;There is a list page.&lt;/p&gt;
&lt;p&gt;There is a search box.&lt;/p&gt;
&lt;p&gt;There is an external data source.&lt;/p&gt;
&lt;p&gt;So the simplest solution is obvious: fetch the data and render it.&lt;/p&gt;
&lt;p&gt;For a demo, that works.&lt;/p&gt;
&lt;p&gt;For a real application, it usually starts creating problems.&lt;/p&gt;
&lt;p&gt;The UI becomes coupled to someone else&apos;s response shape. Errors arrive in a format that is not designed for your product. Query parameters get validated in the component layer. Base URLs appear as strings inside UI code. Eventually, an API key or another secret appears, and suddenly the browser is no longer the right place for that logic.&lt;/p&gt;
&lt;p&gt;That is where a server boundary becomes useful.&lt;/p&gt;
&lt;p&gt;In the Next.js App Router, Route Handlers can provide that boundary.&lt;/p&gt;
&lt;p&gt;They let you create internal API routes inside the &lt;code&gt;app&lt;/code&gt; directory using the standard Web &lt;code&gt;Request&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; APIs. They can handle methods such as &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, and &lt;code&gt;OPTIONS&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In practice, Route Handlers allow a Next.js app to contain a small backend layer without creating a separate backend service.&lt;/p&gt;
&lt;p&gt;That layer can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;proxy external APIs&lt;/li&gt;
&lt;li&gt;normalize responses for the UI&lt;/li&gt;
&lt;li&gt;validate query parameters&lt;/li&gt;
&lt;li&gt;hide environment variables&lt;/li&gt;
&lt;li&gt;protect secrets&lt;/li&gt;
&lt;li&gt;return predictable error formats&lt;/li&gt;
&lt;li&gt;define an internal API contract&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes Route Handlers especially useful for small full-stack projects, dashboards, admin panels, prototypes, and production apps that do not yet need a separate backend.&lt;/p&gt;
&lt;h2&gt;Where Direct Fetch Starts to Hurt&lt;/h2&gt;
&lt;p&gt;A typical first version might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use client&apos;;

import { useEffect, useState } from &apos;react&apos;;

export default function ProductsPage() {
  const [products, setProducts] = useState([]);

  useEffect(() =&amp;gt; {
    async function loadProducts() {
      const response = await fetch(
        &apos;https://dummyjson.com/products?limit=12&apos;
      );

      const data = await response.json();

      setProducts(data.products);
    }

    loadProducts();
  }, []);

  return (
    &amp;lt;ul&amp;gt;
      {products.map(product =&amp;gt; (
        &amp;lt;li key={product.id}&amp;gt;
          {product.title}
        &amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing terrible here.&lt;/p&gt;
&lt;p&gt;The code is short.&lt;/p&gt;
&lt;p&gt;It works.&lt;/p&gt;
&lt;p&gt;But architecturally, the component now knows too much.&lt;/p&gt;
&lt;p&gt;It knows the external URL.&lt;/p&gt;
&lt;p&gt;It knows the external response shape.&lt;/p&gt;
&lt;p&gt;It knows that the products live in &lt;code&gt;data.products&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It will probably handle external API errors directly.&lt;/p&gt;
&lt;p&gt;If the third-party API changes a field name, the UI breaks.&lt;/p&gt;
&lt;p&gt;If authentication is added later, this client-side request becomes the wrong place for it.&lt;/p&gt;
&lt;p&gt;The real question is:&lt;/p&gt;
&lt;p&gt;Why should the UI know anything about &lt;code&gt;dummyjson.com&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The interface does not need an external service.&lt;/p&gt;
&lt;p&gt;It needs a clean list of products in an internal format.&lt;/p&gt;
&lt;h2&gt;Route Handler as an Internal API Boundary&lt;/h2&gt;
&lt;p&gt;Instead of calling the external API directly from the component, create an internal route:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/app/api/products/route.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the application its own server endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/api/products
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI can call this internal endpoint, while the Route Handler talks to the external API.&lt;/p&gt;
&lt;p&gt;Here is a cleaner implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/app/api/products/route.js

import { getProductsApiBaseUrl } from &apos;@/app/_config/env&apos;;

function createErrorResponse(message, status, details = null) {
  return Response.json(
    {
      ok: false,
      error: message,
      details,
    },
    { status }
  );
}

function parseNumberParam(searchParams, name, options) {
  const rawValue = searchParams.get(name);

  if (rawValue === null) {
    return {
      ok: true,
      value: options.defaultValue,
    };
  }

  if (!/^\d+$/.test(rawValue)) {
    return {
      ok: false,
      error: {
        field: name,
        message: `${name} must be a positive integer`,
      },
    };
  }

  const value = Number(rawValue);

  if (value &amp;lt; options.min || value &amp;gt; options.max) {
    return {
      ok: false,
      error: {
        field: name,
        message: `${name} must be between ${options.min} and ${options.max}`,
      },
    };
  }

  return {
    ok: true,
    value,
  };
}

function mapProduct(product) {
  return {
    id: product.id,
    title: product.title,
    price: product.price,
    thumbnail: product.thumbnail,
    category: product.category,
    brand: product.brand ?? null,
    rating: product.rating ?? null,
  };
}

function normalizeProductsResponse(rawData, params) {
  const products = Array.isArray(rawData.products)
    ? rawData.products
    : [];

  return {
    items: products.map(mapProduct),
    page: {
      total: Number(rawData.total ?? products.length),
      limit: params.limit,
      skip: params.skip,
    },
    query: {
      q: params.q,
    },
  };
}

export async function GET(request) {
  const apiBaseUrl = getProductsApiBaseUrl();
  const url = new URL(request.url);

  const q = url.searchParams.get(&apos;q&apos;)?.trim() ?? &apos;&apos;;

  const limitResult = parseNumberParam(
    url.searchParams,
    &apos;limit&apos;,
    {
      defaultValue: 12,
      min: 1,
      max: 100,
    }
  );

  if (!limitResult.ok) {
    return createErrorResponse(
      &apos;Bad Request&apos;,
      400,
      limitResult.error
    );
  }

  const skipResult = parseNumberParam(
    url.searchParams,
    &apos;skip&apos;,
    {
      defaultValue: 0,
      min: 0,
      max: 10000,
    }
  );

  if (!skipResult.ok) {
    return createErrorResponse(
      &apos;Bad Request&apos;,
      400,
      skipResult.error
    );
  }

  const limit = limitResult.value;
  const skip = skipResult.value;

  const upstreamUrl = q
    ? new URL(&apos;/products/search&apos;, apiBaseUrl)
    : new URL(&apos;/products&apos;, apiBaseUrl);

  upstreamUrl.searchParams.set(&apos;limit&apos;, String(limit));
  upstreamUrl.searchParams.set(&apos;skip&apos;, String(skip));

  if (q) {
    upstreamUrl.searchParams.set(&apos;q&apos;, q);
  }

  try {
    const upstreamResponse = await fetch(
      upstreamUrl.toString()
    );

    if (!upstreamResponse.ok) {
      const bodyPreview = await upstreamResponse
        .text()
        .then(text =&amp;gt; text.slice(0, 200))
        .catch(() =&amp;gt; &apos;&apos;);

      return createErrorResponse(
        &apos;Upstream API Error&apos;,
        502,
        {
          status: upstreamResponse.status,
          statusText: upstreamResponse.statusText,
          bodyPreview,
        }
      );
    }

    const rawData = await upstreamResponse.json();

    const data = normalizeProductsResponse(
      rawData,
      {
        q,
        limit,
        skip,
      }
    );

    return Response.json(
      {
        ok: true,
        ...data,
      },
      { status: 200 }
    );
  } catch (error) {
    return createErrorResponse(
      &apos;Failed to load products&apos;,
      500,
      {
        name: error?.name ?? &apos;Error&apos;,
        message: error?.message ?? String(error),
      }
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This route does more than forward the request.&lt;/p&gt;
&lt;p&gt;It creates a contract.&lt;/p&gt;
&lt;p&gt;The browser no longer depends on the external API directly. It depends on your own &lt;code&gt;/api/products&lt;/code&gt; route.&lt;/p&gt;
&lt;p&gt;That is the important architectural shift.&lt;/p&gt;
&lt;h2&gt;Normalization Is More Useful Than Proxying&lt;/h2&gt;
&lt;p&gt;A weak Route Handler simply returns the external JSON as-is.&lt;/p&gt;
&lt;p&gt;That is technically a proxy, but it does not solve much.&lt;/p&gt;
&lt;p&gt;A stronger Route Handler normalizes the response into the shape the UI actually needs.&lt;/p&gt;
&lt;p&gt;External response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  products: [
    {
      id: 1,
      title: &apos;Phone&apos;,
      description: &apos;...&apos;…</content:encoded></item><item><title>Friday Links #39 — JavaScript Releases &amp; Web Experiments</title><link>https://jsdev.space/friday/friday-39/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-39/</guid><description>A weekly roundup of AI agents, JavaScript releases, developer tools, browser updates, open-source projects, and engineering insights.</description><pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-39.png&quot; alt=&quot;Friday Links #39&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another busy week in software development.&lt;/p&gt;
&lt;p&gt;AI tooling continued evolving at an absurd pace, JavaScript frameworks shipped new releases, browser vendors rolled out experimental features, and open-source maintainers somehow found time to launch another wave of libraries, runtimes, and developer tools.&lt;/p&gt;
&lt;p&gt;This edition collects the releases, discussions, tutorials, experiments, and projects that stood out during the week.&lt;/p&gt;
&lt;p&gt;Whether you&apos;re following React, TypeScript, AI agents, browser APIs, infrastructure, or modern web tooling, here are the links worth opening in a new tab.&lt;/p&gt;
&lt;h2&gt;🧠 Language &amp;amp; Runtime Updates&lt;/h2&gt;
&lt;h3&gt;TypeScript 7.0 Beta Continues To Generate Excitement&lt;/h3&gt;
&lt;p&gt;The TypeScript team continues refining TypeScript 7.0 and its new Go-based compiler architecture. Early benchmarks and community testing suggest substantial performance improvements for large codebases, monorepos, and editor tooling. The transition marks one of the most significant architectural changes in TypeScript&apos;s history.&lt;/p&gt;
&lt;h3&gt;VoidZero Joins Cloudflare&lt;/h3&gt;
&lt;p&gt;One of the biggest JavaScript ecosystem stories this week: the team behind Vite, Rolldown, Oxc, Vitest, and VoidZero is joining Cloudflare. The move could accelerate development across the next generation of JavaScript tooling and infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/voidzero-joins-cloudflare/&quot;&gt;The Cloudflare Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;npm 12 Will Disable Install Scripts By Default&lt;/h3&gt;
&lt;p&gt;npm is preparing a major security-focused change. Future versions will no longer execute &lt;code&gt;preinstall&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt;, and &lt;code&gt;postinstall&lt;/code&gt; scripts automatically by default. The goal is to reduce supply-chain attack risks and improve package installation security.&lt;/p&gt;
&lt;p&gt;Node.js Security Releases Land Across Multiple Branches&lt;/p&gt;
&lt;p&gt;Node.js shipped security updates for multiple active release lines, including fixes for high-severity vulnerabilities. Teams running production workloads should review the latest security advisories and update accordingly.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/npm/rfcs&quot;&gt;rfcs&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Node.js 24 Continues To Improve Developer Experience&lt;/h3&gt;
&lt;p&gt;Recent updates to Node.js 24 introduced support for UUID v7 generation, additional testing utilities, and improvements across the platform. The project continues pushing toward a more batteries-included developer experience.&lt;/p&gt;
&lt;h3&gt;Node.js Security Releases Land Across Multiple Versions&lt;/h3&gt;
&lt;p&gt;The Node.js team released security updates across several supported release lines, including Node.js 22, 24, and 26. The updates address multiple high-severity vulnerabilities affecting core dependencies and runtime components. Teams running production workloads should review the advisories and upgrade as soon as possible to ensure their applications remain protected.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/en/blog/vulnerability/june-2026-security-releases&quot;&gt;Node.js Security Releases&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nextweekai.com/blog/build-chatgpt-from-scratch-guide/&quot;&gt;How to Build ChatGPT From Scratch: Understanding LLMs Step by Step&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tanstack.com/blog/tanstack-table-v9-typescript-performance&quot;&gt;TypeScript Performance in TanStack Table V9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vercel.com/changelog/introducing-eve-an-open-source-agent-framework&quot;&gt;Introducing eve, an open-source agent framework&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.debugbear.com/blog/lazy-loading-performance&quot;&gt;How to Use Lazy Loading Without Hurting Web Performance&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kittygiraudel.com/2026/04/09/an-interactive-cover-component/&quot;&gt;An Interactive Cover Component&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://olliewilliams.xyz/blog/streaming-html/&quot;&gt;Streaming HTML&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://roman.pt/posts/linkedin-backdoor/&quot;&gt;A backdoor in a LinkedIn job offer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.react.doctor/blog/the-problem-with-useeffect&quot;&gt;The problem with useEffect&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://evilmartians.com/chronicles/ai-assisted-engineers-are-burning-out-is-this-fine&quot;&gt;AI-assisted engineers are burning out, is this fine?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2026/06/how-make-design-system-ai-ready/&quot;&gt;How To Make Your Design System AI-Ready&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/introducing-mdn-mcp-server/&quot;&gt;Introducing the MDN MCP server&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zed.dev/blog/introducing-deltadb&quot;&gt;Software Is Made Between Commits&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nextweekai.com/&quot;&gt;NextWeekAI&lt;/a&gt; - Curated directory of cutting-edge AI tools.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://chrome.dev/view-transitions-toolkit/&quot;&gt;View Transitions Toolkit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A collection of utility functions to more easily work with View Transitions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sindresorhus/eslint-plugin-unicorn&quot;&gt;eslint-plugin-unicorn 67.0&lt;/a&gt; -  Adds Even More Ways to Improve Your Code&lt;/p&gt;
&lt;p&gt;Nodemailer 9.0 Tightens Email Security in Node.js&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodemailer.com/&quot;&gt;Nodemailer 9.0&lt;/a&gt; is out, bringing updates to one of the most widely used email libraries in the Node.js ecosystem. The package continues to support SMTP, Amazon SES, and Sendmail-based delivery, while version 9.0 adds a notable security improvement: TLS connections are now validated by default when fetching remote content. For projects that generate rich emails with external assets, this is a small but important hardening update.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tabulator-tables/tabulator&quot;&gt;Tabulator 6.5&lt;/a&gt; - Easy to use, simple to code, fully featured, interactive JavaScript tables and data grids.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/github/relative-time-element&quot;&gt;&lt;code&gt;&amp;lt;relative-time&amp;gt;&lt;/code&gt; element&lt;/a&gt; - Web component extensions to the standard &lt;code&gt;&amp;lt;time&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xem.github.io/miniPixelFont/js13k.html&quot;&gt;MiniPixelFont generator&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://react.foundation&quot;&gt;React Foundation Launches to Support the Future of React&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The newly announced React Foundation aims to provide long-term sustainability for one of the world&apos;s most widely used frontend ecosystems. The organization introduces a community-driven governance model focused on transparent decision-making, direct funding for maintainers, and financial support for critical open-source projects. By creating a dedicated foundation, the React community hopes to ensure the continued health, stability, and growth of the ecosystem for years to come.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://isitagentready.com&quot;&gt;Is Your Site Agent-Ready?&lt;/a&gt; - A simple tool to check if your website is ready for AI agents.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/leonxlnx/taste-skill&quot;&gt;Taste Skill&lt;/a&gt; -  Helps frontend projects escape the generic SaaS look&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/kessler/gemma-gem&quot;&gt;Gemma Gem&lt;/a&gt; - Your personal AI assistant living right inside the browser. Gemma Gem runs Google&apos;s Gemma 4 model entirely on-device via WebGPU — no API keys, no cloud, no data leaving your machine. It can read pages, click buttons, fill forms, run JavaScript, and answer questions about any site you visit.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/wonderwhy-er/DesktopCommanderMCP&quot;&gt;Desktop Commander MCP&lt;/a&gt; - This is MCP server for Claude that gives it terminal control, file system search and diff file editing capabilities&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://depsguard.com&quot;&gt;DepsGuard&lt;/a&gt; - Hardens Package Manager Configs Against Supply-Chain Attacks&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;h3&gt;zod-compiler Brings Zero-Overhead Validation to Zod&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/gajus/zod-compiler&quot;&gt;zod-compiler&lt;/a&gt; is a new build-time optimization tool that transforms existing Zod schemas into highly optimized validators during bundling. Designed to work with Vite, Webpack, and other modern build systems, it preserves the familiar Zod API while generating tree-shakeable validation code with virtually no runtime ov…</content:encoded></item><item><title>35 React Interview Questions Senior Developers Still Get Wrong</title><link>https://jsdev.space/react-interview-questions-2026/</link><guid isPermaLink="true">https://jsdev.space/react-interview-questions-2026/</guid><description>A practical collection of React 19 interview questions covering rendering, hooks, Context, React Compiler, Server Components, performance optimization...</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React interviews have changed dramatically over the past few years.&lt;/p&gt;
&lt;p&gt;A few years ago, candidates were asked about class components, lifecycle methods, and whether they could recite the difference between &lt;code&gt;componentDidMount()&lt;/code&gt; and &lt;code&gt;componentDidUpdate()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Today, those questions rarely matter.&lt;/p&gt;
&lt;p&gt;Modern React interviews focus on a completely different set of topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rendering behavior&lt;/li&gt;
&lt;li&gt;State management&lt;/li&gt;
&lt;li&gt;React 19 features&lt;/li&gt;
&lt;li&gt;Server Components&lt;/li&gt;
&lt;li&gt;Performance optimization&lt;/li&gt;
&lt;li&gt;Architecture decisions&lt;/li&gt;
&lt;li&gt;Developer experience&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interviewers want to understand whether a developer can build and maintain large React applications—not whether they memorized API documentation.&lt;/p&gt;
&lt;p&gt;This guide covers the React questions that appear most frequently in modern interviews and explains the reasoning behind the answers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Understanding How React Actually Renders&lt;/h2&gt;
&lt;p&gt;Many React developers can build applications successfully without fully understanding what happens after state changes.&lt;/p&gt;
&lt;p&gt;Unfortunately, this becomes obvious during interviews.&lt;/p&gt;
&lt;h3&gt;What Is React?&lt;/h3&gt;
&lt;p&gt;The simplest answer is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;React is a JavaScript library for building user interfaces using reusable components.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A better answer explains why developers choose React:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;React allows developers to describe UI declaratively. Instead of manually updating the DOM, components describe what the UI should look like for a given state, and React handles the updates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is JSX?&lt;/h3&gt;
&lt;p&gt;JSX is a syntax extension that allows HTML-like syntax inside JavaScript.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function WelcomeCard() {
  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;Welcome&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Thanks for joining.&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During compilation, JSX is transformed into React element creation calls.&lt;/p&gt;
&lt;p&gt;Modern React projects rarely require developers to think about this transformation, but interviewers still occasionally ask about it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is the Virtual DOM?&lt;/h3&gt;
&lt;p&gt;One of the most common React interview questions.&lt;/p&gt;
&lt;p&gt;The Virtual DOM is an in-memory representation of the UI.&lt;/p&gt;
&lt;p&gt;When state changes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;React creates a new virtual tree.&lt;/li&gt;
&lt;li&gt;Compares it with the previous tree.&lt;/li&gt;
&lt;li&gt;Calculates the minimum required updates.&lt;/li&gt;
&lt;li&gt;Updates the browser DOM efficiently.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Many candidates incorrectly claim that the Virtual DOM is &quot;faster than the DOM.&quot;&lt;/p&gt;
&lt;p&gt;That&apos;s not entirely true.&lt;/p&gt;
&lt;p&gt;The real advantage is that React minimizes expensive DOM operations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is Reconciliation?&lt;/h3&gt;
&lt;p&gt;Reconciliation is the process React uses to compare two virtual trees.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Counter({ count }: Props) {
  return &amp;lt;span&amp;gt;{count}&amp;lt;/span&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;count&lt;/code&gt; changes from &lt;code&gt;5&lt;/code&gt; to &lt;code&gt;6&lt;/code&gt;, React does not rebuild the entire component tree.&lt;/p&gt;
&lt;p&gt;It identifies the smallest possible change and updates only the affected node.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Keys: The Question That Appears Everywhere&lt;/h2&gt;
&lt;h3&gt;Why Are Keys Important?&lt;/h3&gt;
&lt;p&gt;Keys help React identify elements between renders.&lt;/p&gt;
&lt;p&gt;Bad:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  users.map((user, index) =&amp;gt; (
    &amp;lt;UserRow
      key={index}
      user={user}
    /&amp;gt;
  ));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  users.map((user) =&amp;gt; (
    &amp;lt;UserRow
      key={user.id}
      user={user}
    /&amp;gt;
  ));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using array indexes can cause:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Incorrect state preservation&lt;/li&gt;
&lt;li&gt;Broken animations&lt;/li&gt;
&lt;li&gt;Unexpected form behavior&lt;/li&gt;
&lt;li&gt;Unnecessary re-renders&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This question appears surprisingly often in interviews.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Modern React Hooks Questions&lt;/h2&gt;
&lt;p&gt;Hooks remain one of the most important interview topics.&lt;/p&gt;
&lt;h3&gt;When Should You Use useState?&lt;/h3&gt;
&lt;p&gt;Use state when data affects rendering.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [isOpen, setIsOpen] =
  useState(false);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modals&lt;/li&gt;
&lt;li&gt;Form inputs&lt;/li&gt;
&lt;li&gt;Selected tabs&lt;/li&gt;
&lt;li&gt;UI toggles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If changing a value should trigger a UI update, it belongs in state.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;When Should You Use useRef Instead of useState?&lt;/h3&gt;
&lt;p&gt;A common senior-level question.&lt;/p&gt;
&lt;p&gt;Not every value belongs in state.&lt;/p&gt;
&lt;p&gt;Bad:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [socket, setSocket] =
  useState&amp;lt;WebSocket | null&amp;gt;(
    null
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Better:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const socketRef =
  useRef&amp;lt;WebSocket | null&amp;gt;(
    null
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good use cases for refs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebSocket instances&lt;/li&gt;
&lt;li&gt;Timers&lt;/li&gt;
&lt;li&gt;DOM elements&lt;/li&gt;
&lt;li&gt;External libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Refs do not trigger re-renders.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is the Biggest useEffect Mistake?&lt;/h3&gt;
&lt;p&gt;Many developers use effects for derived state.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [fullName, setFullName] =
  useState(&apos;&apos;);

useEffect(() =&amp;gt; {
  setFullName(
    `${firstName} ${lastName}`
  );
}, [firstName, lastName]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is unnecessary.&lt;/p&gt;
&lt;p&gt;Better:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const fullName =
  `${firstName} ${lastName}`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a value can be calculated during rendering, it usually should be.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;useEffect vs useLayoutEffect&lt;/h3&gt;
&lt;p&gt;A classic interview question.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;useEffect&lt;/td&gt;
&lt;td&gt;After paint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;useLayoutEffect&lt;/td&gt;
&lt;td&gt;Before paint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;useLayoutEffect(() =&amp;gt; {
  const width =
    element.current?.offsetWidth;
}, []);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;useLayoutEffect&lt;/code&gt; when measuring or synchronizing with the DOM before the browser paints.&lt;/p&gt;
&lt;p&gt;For most side effects, use &lt;code&gt;useEffect&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React 19 Interview Questions&lt;/h2&gt;
&lt;p&gt;This is where most modern interviews focus.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Are Actions?&lt;/h3&gt;
&lt;p&gt;Actions simplify async state management.&lt;/p&gt;
&lt;p&gt;Before React 19:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [loading, setLoading] =
  useState(false);

const handleSubmit =
  async () =&amp;gt; {
    setLoading(true);

    try {
      await saveProfile();
    } finally {
      setLoading(false);
    }
  };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;React 19:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [
  profile,
  submitProfile,
  pending,
] = useActionState(
  updateProfile,
  null
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://jsdev.space/howto/react19-useactionstate-guide/&quot;&gt;Understanding React 19 useActionState From First Principles&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Less boilerplate.&lt;/p&gt;
&lt;p&gt;Better UX.&lt;/p&gt;
&lt;p&gt;Built-in pending states.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is useOptimistic?&lt;/h3&gt;
&lt;p&gt;One of the most popular React 19 questions.&lt;/p&gt;
&lt;p&gt;It allows optimistic updates before a request finishes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [
  messages,
  addOptimisticMessage,
] = useOptimistic(
  chatMessages,
  (current, text) =&amp;gt; [
    ...current,
    {
      id: crypto.randomUUID(),
      text,
      pending: true,
    },
  ]
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI updates immediately.&lt;/p&gt;
&lt;p&gt;The server response arrives later.&lt;/p&gt;
&lt;p&gt;Users perceive the application as much faster.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is React Compiler?&lt;/h3&gt;
&lt;p&gt;React Compiler is one of the biggest React changes in years.&lt;/p&gt;
&lt;p&gt;Historically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const activeUsers =
  useMemo(
    () =&amp;gt;
      users.filter(
        (user) =&amp;gt; user.active
      ),
    [users]
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With React Compiler:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const activeUsers =
  users.filter(
    (user) =&amp;gt; user.active
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler can automatically insert memoization where safe.&lt;/p&gt;
&lt;p&gt;As adoption increases, many manual optimization patterns may disappear.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;What Is the Difference Between Server Components and Client Components?&lt;/h3&gt;
&lt;p&gt;Server Components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run on the server&lt;/li&gt;
&lt;li&gt;Send no JavaScript&lt;/li&gt;
&lt;li&gt;Can access databases directly&lt;/li&gt;
&lt;l…</content:encoded></item><item><title>Howto Master React 19 useActionState for Async Forms</title><link>https://jsdev.space/howto/react19-useactionstate-guide/</link><guid isPermaLink="true">https://jsdev.space/howto/react19-useactionstate-guide/</guid><description>Learn how React 19 useActionState simplifies async forms, server actions, optimistic UI updates, and state management with less boilerplate.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React developers have spent years writing the same code over and over again.&lt;/p&gt;
&lt;p&gt;A form submits data.&lt;/p&gt;
&lt;p&gt;The UI enters a loading state.&lt;/p&gt;
&lt;p&gt;Errors must be handled.&lt;/p&gt;
&lt;p&gt;Success messages appear.&lt;/p&gt;
&lt;p&gt;Buttons become disabled.&lt;/p&gt;
&lt;p&gt;Then everything needs to be reset.&lt;/p&gt;
&lt;p&gt;The implementation rarely changes.&lt;/p&gt;
&lt;p&gt;Only the business logic does.&lt;/p&gt;
&lt;p&gt;A typical React form before React 19 often looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function NewsletterForm() {
  const [pending, setPending] =
    useState(false);

  const [error, setError] =
    useState&amp;lt;string | null&amp;gt;(null);

  const [success, setSuccess] =
    useState(false);

  async function handleSubmit(
    event: React.FormEvent
  ) {
    event.preventDefault();

    setPending(true);
    setError(null);

    try {
      const form =
        new FormData(
          event.currentTarget
        );

      await subscribeUser(form);

      setSuccess(true);
    } catch (err) {
      setError(
        &apos;Subscription failed&apos;
      );
    } finally {
      setPending(false);
    }
  }

  return (
    &amp;lt;form onSubmit={handleSubmit}&amp;gt;
      &amp;lt;input
        name=&quot;email&quot;
        type=&quot;email&quot;
      /&amp;gt;

      &amp;lt;button disabled={pending}&amp;gt;
        {pending
          ? &apos;Submitting...&apos;
          : &apos;Subscribe&apos;}
      &amp;lt;/button&amp;gt;

      {error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;{error}&amp;lt;/p&amp;gt;}
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing wrong with this code.&lt;/p&gt;
&lt;p&gt;The problem is repetition.&lt;/p&gt;
&lt;p&gt;Every form in every application eventually grows into a collection of loading flags, error states, success states, and submission handlers.&lt;/p&gt;
&lt;p&gt;React 19 introduces a different approach.&lt;/p&gt;
&lt;h2&gt;What Is useActionState?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useActionState&lt;/code&gt; is a React 19 hook designed to manage asynchronous state transitions.&lt;/p&gt;
&lt;p&gt;Instead of manually tracking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;loading state&lt;/li&gt;
&lt;li&gt;success state&lt;/li&gt;
&lt;li&gt;error state&lt;/li&gt;
&lt;li&gt;submission lifecycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;React manages the workflow automatically.&lt;/p&gt;
&lt;p&gt;The API is surprisingly small:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [
  state,
  submitAction,
  isPending,
] = useActionState(
  action,
  initialState
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three values.&lt;/p&gt;
&lt;p&gt;One hook.&lt;/p&gt;
&lt;p&gt;A significant reduction in boilerplate.&lt;/p&gt;
&lt;h2&gt;The Simplest Example&lt;/h2&gt;
&lt;p&gt;Imagine a newsletter subscription form.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type FormState = {
  success: boolean;
  message: string;
};

const initialState: FormState = {
  success: false,
  message: &apos;&apos;,
};

async function subscribe(
  previousState: FormState,
  formData: FormData
): Promise&amp;lt;FormState&amp;gt; {
  const email =
    formData.get(&apos;email&apos;);

  await new Promise(
    resolve =&amp;gt;
      setTimeout(resolve, 1000)
  );

  return {
    success: true,
    message: `Subscribed ${email}`,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;useActionState&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function NewsletterForm() {
  const [
    state,
    formAction,
    pending,
  ] = useActionState(
    subscribe,
    initialState
  );

  return (
    &amp;lt;form action={formAction}&amp;gt;
      &amp;lt;input
        name=&quot;email&quot;
        type=&quot;email&quot;
      /&amp;gt;

      &amp;lt;button disabled={pending}&amp;gt;
        {pending
          ? &apos;Submitting...&apos;
          : &apos;Subscribe&apos;}
      &amp;lt;/button&amp;gt;

      {state.message &amp;amp;&amp;amp; (
        &amp;lt;p&amp;gt;{state.message}&amp;lt;/p&amp;gt;
      )}
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is dramatically cleaner.&lt;/p&gt;
&lt;p&gt;No &lt;code&gt;preventDefault&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;No loading state.&lt;/p&gt;
&lt;p&gt;No manual state synchronization.&lt;/p&gt;
&lt;p&gt;No try/catch boilerplate.&lt;/p&gt;
&lt;p&gt;React handles the lifecycle automatically.&lt;/p&gt;
&lt;h2&gt;Why React Built This Hook&lt;/h2&gt;
&lt;p&gt;At first glance, &lt;code&gt;useActionState&lt;/code&gt; appears to be a form utility.&lt;/p&gt;
&lt;p&gt;It is not.&lt;/p&gt;
&lt;p&gt;Internally, React treats it as part of a much larger concept:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Actions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An Action represents a user intent that changes application state.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;submitting a form&lt;/li&gt;
&lt;li&gt;publishing a post&lt;/li&gt;
&lt;li&gt;deleting a record&lt;/li&gt;
&lt;li&gt;updating a profile&lt;/li&gt;
&lt;li&gt;synchronizing data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;React 19 introduces several APIs built around this concept:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;useActionState&lt;/li&gt;
&lt;li&gt;useOptimistic&lt;/li&gt;
&lt;li&gt;useTransition&lt;/li&gt;
&lt;li&gt;useFormStatus&lt;/li&gt;
&lt;li&gt;Server Actions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together they form a complete system for handling asynchronous state.&lt;/p&gt;
&lt;h2&gt;Understanding the State Flow&lt;/h2&gt;
&lt;p&gt;Traditional React applications often separate state and side effects.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;User Click
     ↓
Event Handler
     ↓
Loading State
     ↓
API Request
     ↓
Success/Error
     ↓
UI Update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;useActionState&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;User Action
     ↓
Action Function
     ↓
React
     ↓
Updated State
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;React becomes responsible for coordinating the transition.&lt;/p&gt;
&lt;p&gt;The developer focuses on business logic.&lt;/p&gt;
&lt;h2&gt;Working with Validation&lt;/h2&gt;
&lt;p&gt;A common use case is form validation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type LoginState = {
  error: string | null;
};

async function loginAction(
  previousState: LoginState,
  formData: FormData
): Promise&amp;lt;LoginState&amp;gt; {
  const email =
    String(
      formData.get(&apos;email&apos;)
    );

  if (!email.includes(&apos;@&apos;)) {
    return {
      error:
        &apos;Please enter a valid email&apos;,
    };
  }

  return {
    error: null,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [
  state,
  submit,
  pending,
] = useActionState(
  loginAction,
  {
    error: null,
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Validation errors become part of state instead of being handled separately.&lt;/p&gt;
&lt;p&gt;This creates a more predictable data flow.&lt;/p&gt;
&lt;h2&gt;The Relationship Between useActionState and Server Actions&lt;/h2&gt;
&lt;p&gt;One of the most exciting aspects of React 19 is Server Actions.&lt;/p&gt;
&lt;p&gt;A server action can be used directly with &lt;code&gt;useActionState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use server&apos;;

export async function createPost(
  previousState,
  formData
) {
  const title =
    formData.get(&apos;title&apos;);

  await db.post.create({
    data: {
      title,
    },
  });

  return {
    success: true,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Client component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [
  state,
  formAction,
  pending,
] = useActionState(
  createPost,
  {
    success: false,
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates an extremely clean full-stack workflow.&lt;/p&gt;
&lt;p&gt;No custom API route.&lt;/p&gt;
&lt;p&gt;No fetch request.&lt;/p&gt;
&lt;p&gt;No manual serialization.&lt;/p&gt;
&lt;p&gt;No client-side request management.&lt;/p&gt;
&lt;p&gt;React handles everything.&lt;/p&gt;
&lt;h2&gt;useActionState vs useReducer&lt;/h2&gt;
&lt;p&gt;Many developers compare these APIs.&lt;/p&gt;
&lt;p&gt;They solve different problems.&lt;/p&gt;
&lt;h3&gt;useReducer&lt;/h3&gt;
&lt;p&gt;Best for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;synchronous state updates&lt;/li&gt;
&lt;li&gt;complex UI state&lt;/li&gt;
&lt;li&gt;finite state machines&lt;/li&gt;
&lt;li&gt;reducers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dispatch({
  type: &apos;ADD_TODO&apos;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;useActionState&lt;/h3&gt;
&lt;p&gt;Best for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;forms&lt;/li&gt;
&lt;li&gt;API requests&lt;/li&gt;
&lt;li&gt;server mutations&lt;/li&gt;
&lt;li&gt;async workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;formAction(formData);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A useful rule:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the state change requires I/O, useActionState is usually the better choice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;useActionState and useOptimistic&lt;/h2&gt;
&lt;p&gt;The real power appears when these hooks work together.&lt;/p&gt;
&lt;p&gt;Imagine a comment form.&lt;/p&gt;
&lt;p&gt;Instead of waiting for the server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;User submits
      ↓
Wait 2 seconds
      ↓
Comment appears
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can show the comment immediately.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [
  comments,
  addOptimisticComment,
] = useOptimistic(
  initialComments,
  (
    currentComments,
    text
  ) =&amp;gt; [
    ...currentComments,
    {
      id: crypto.randomUUID(),
      text,
      pending: true,
    },
  ]
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI feels instant.&lt;/p&gt;
&lt;p&gt;The server catches up later.&lt;/p&gt;
&lt;p&gt;This is one of the biggest user experience improvements enabled by React 19.&lt;/p&gt;
&lt;h2&gt;Performance Considerations&lt;/h2&gt;
&lt;p&gt;One hidden advantage of &lt;code&gt;useActionState&lt;/code&gt; is its integration with Rea…</content:encoded></item><item><title>The Forgotten Security Goldmine Hiding in Chrome DevTools</title><link>https://jsdev.space/forgotten-devtools-source-security/</link><guid isPermaLink="true">https://jsdev.space/forgotten-devtools-source-security/</guid><description>Why Chrome DevTools Source tab still helps developers, QA engineers, and security researchers discover exposed endpoints, secrets, and hidden application logic.</description><pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s 2026.&lt;/p&gt;
&lt;p&gt;Autonomous delivery robots move through city streets. AI-powered QA systems automatically scan applications for common vulnerabilities. Modern frameworks can power everything from a local bakery website to a massive healthcare platform serving millions of users.&lt;/p&gt;
&lt;p&gt;Software development has never been more automated.&lt;/p&gt;
&lt;p&gt;Yet one of the oldest tools available in every browser continues to uncover security issues that expensive scanners still miss.&lt;/p&gt;
&lt;p&gt;I&apos;m talking about the &lt;strong&gt;Source&lt;/strong&gt; tab inside Chrome DevTools.&lt;/p&gt;
&lt;p&gt;For many developers, it feels like a relic from an earlier era of frontend development. Most people open DevTools for the Network panel, Console, Performance tools, or React DevTools. Few spend much time manually exploring JavaScript bundles and source files.&lt;/p&gt;
&lt;p&gt;That is exactly why it remains useful.&lt;/p&gt;
&lt;p&gt;When used carefully, the Source tab can reveal everything from forgotten debugging endpoints to sensitive credentials accidentally exposed during development. Sometimes you&apos;ll find nothing more than harmless internal tooling. Other times you&apos;ll discover security issues that deserve immediate attention.&lt;/p&gt;
&lt;p&gt;The goal isn&apos;t to hunt for vulnerabilities in a specific company or service.&lt;/p&gt;
&lt;p&gt;The goal is to understand a simple reality:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Everything shipped to the browser is visible to the browser.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And that makes source analysis surprisingly valuable even in 2026.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Source Tab Isn&apos;t Dead&lt;/h2&gt;
&lt;p&gt;Many engineers assume that modern build pipelines have solved this problem.&lt;/p&gt;
&lt;p&gt;After all, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bundlers&lt;/li&gt;
&lt;li&gt;Tree shaking&lt;/li&gt;
&lt;li&gt;Minification&lt;/li&gt;
&lt;li&gt;Source maps&lt;/li&gt;
&lt;li&gt;Secret scanning tools&lt;/li&gt;
&lt;li&gt;Static analysis&lt;/li&gt;
&lt;li&gt;AI code reviews&lt;/li&gt;
&lt;li&gt;Security scanners&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Surely nothing interesting remains visible to users anymore.&lt;/p&gt;
&lt;p&gt;In reality, that&apos;s not how software gets built.&lt;/p&gt;
&lt;p&gt;Large applications contain thousands of files and hundreds of contributors. Features move quickly. Debugging code gets added under pressure. Temporary workarounds become permanent solutions.&lt;/p&gt;
&lt;p&gt;Over time, small mistakes accumulate.&lt;/p&gt;
&lt;p&gt;A forgotten endpoint here.&lt;/p&gt;
&lt;p&gt;A hardcoded key there.&lt;/p&gt;
&lt;p&gt;An internal feature flag somewhere else.&lt;/p&gt;
&lt;p&gt;Most automated scanners look for known patterns.&lt;/p&gt;
&lt;p&gt;Humans are still remarkably good at finding things that don&apos;t fit a predefined rule.&lt;/p&gt;
&lt;p&gt;That&apos;s where manual source analysis continues to shine.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Simple Source Inspection Workflow&lt;/h2&gt;
&lt;p&gt;Let&apos;s use a public website as an example.&lt;/p&gt;
&lt;p&gt;Not because we&apos;re trying to find vulnerabilities there, but because the workflow is universal.&lt;/p&gt;
&lt;p&gt;The process is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Chrome DevTools.&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Sources&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Explore the loaded JavaScript files.&lt;/li&gt;
&lt;li&gt;Look for files with suspicious names.&lt;/li&gt;
&lt;li&gt;Search through code for common indicators.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Files worth examining often contain names like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;api
helper
debug
test
internal
admin
sandbox
feature-flags
experimental
dev-tools
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[IMAGE: Screenshot placeholder showing Chrome DevTools Sources panel with multiple JavaScript bundles.]&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/chrome-devtools-jsdev.png&quot; alt=&quot;Screenshot placeholder showing Chrome DevTools Sources panel with a file named &amp;quot;debug.js&amp;quot; highlighted.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When a file catches your attention, search for terms such as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;api
token
secret
key
auth
admin
debug
internal
endpoint
staging
password
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modern applications frequently contain thousands of lines of bundled code.&lt;/p&gt;
&lt;p&gt;You don&apos;t need to read every line.&lt;/p&gt;
&lt;p&gt;Instead, think like an investigator looking for clues.&lt;/p&gt;
&lt;p&gt;You&apos;re searching for unusual behavior, hidden functionality, or information that doesn&apos;t belong in a public client application.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What Do People Actually Find?&lt;/h2&gt;
&lt;p&gt;Over the years I&apos;ve seen developers uncover all sorts of things inside frontend code.&lt;/p&gt;
&lt;p&gt;Some findings are harmless.&lt;/p&gt;
&lt;p&gt;Others are serious.&lt;/p&gt;
&lt;p&gt;The most common categories tend to be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Internal or undocumented API endpoints.&lt;/li&gt;
&lt;li&gt;Secrets accidentally exposed to the client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s look at both.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Undocumented Internal APIs&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Risk level: Medium to Critical&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is one of the most common findings in modern applications.&lt;/p&gt;
&lt;p&gt;During development, engineers often create helper endpoints for testing, debugging, administrative actions, migrations, or internal tooling.&lt;/p&gt;
&lt;p&gt;Sometimes those endpoints are never removed.&lt;/p&gt;
&lt;p&gt;Sometimes they remain accessible from production code.&lt;/p&gt;
&lt;p&gt;Sometimes they&apos;re protected correctly.&lt;/p&gt;
&lt;p&gt;And sometimes they aren&apos;t.&lt;/p&gt;
&lt;p&gt;A common example looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ADMIN_ENDPOINT = &apos;/api/internal/reset-cache&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fetch(&apos;/api/debug/set-user-role&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The presence of an endpoint alone isn&apos;t necessarily a vulnerability.&lt;/p&gt;
&lt;p&gt;Proper authentication and authorization may still prevent misuse.&lt;/p&gt;
&lt;p&gt;The problem appears when security controls are missing.&lt;/p&gt;
&lt;p&gt;Over the years I&apos;ve encountered examples ranging from amusing to dangerous.&lt;/p&gt;
&lt;p&gt;At the low end, I&apos;ve seen endpoints that allowed users to grant themselves cosmetic forum titles.&lt;/p&gt;
&lt;p&gt;At the high end, I&apos;ve seen internal endpoints capable of modifying inventory counts for marketplace sellers.&lt;/p&gt;
&lt;p&gt;The difference between a curiosity and a critical issue is often just one missing authorization check.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Hidden Endpoints Matter&lt;/h2&gt;
&lt;p&gt;Many teams assume that if an endpoint isn&apos;t documented, nobody will find it.&lt;/p&gt;
&lt;p&gt;That assumption is dangerous.&lt;/p&gt;
&lt;p&gt;If the endpoint appears inside shipped JavaScript, it is effectively public.&lt;/p&gt;
&lt;p&gt;Attackers don&apos;t need documentation.&lt;/p&gt;
&lt;p&gt;They only need visibility.&lt;/p&gt;
&lt;p&gt;This is why security should never rely on obscurity.&lt;/p&gt;
&lt;p&gt;An endpoint must be protected even if every user on Earth knows its URL.&lt;/p&gt;
&lt;p&gt;A practical rule I&apos;ve adopted is simple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Protect debugging endpoints exactly as aggressively as production endpoints.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If an endpoint can change state, expose data, or trigger business logic, it deserves proper authorization controls.&lt;/p&gt;
&lt;p&gt;No exceptions.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Secrets in Frontend Code&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Risk level: Low to Critical&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The second major category is exposed secrets.&lt;/p&gt;
&lt;p&gt;This usually happens because developers use credentials during development and accidentally leave them behind.&lt;/p&gt;
&lt;p&gt;Sometimes the secret appears directly in code.&lt;/p&gt;
&lt;p&gt;Sometimes it appears in comments.&lt;/p&gt;
&lt;p&gt;Sometimes it exists inside a configuration object that was never intended to reach production.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/key-in-js-bundle.png&quot; alt=&quot;Exposed Secret in JavaScript Bundle&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Examples found in real-world applications include:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AWS access keys
Azure credentials
API keys
Payment provider secrets
Email service credentials
Slack tokens
GitHub tokens
Telegram bot tokens
JWT signing secrets
Administrative passwords
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Occasionally you encounter something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const adminPassword = &apos;SuperSecretPassword123&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or even:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// temporary admin account
// admin:password123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, these examples sound ridiculous.&lt;/p&gt;
&lt;p&gt;Yet they still happen.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Exposed Secrets Are Dangerous&lt;/h2&gt;
&lt;p&gt;The consequences vary dramatically depending on the credential.&lt;/p&gt;
&lt;p&gt;An expired key may have no impact.&lt;/p&gt;
&lt;p&gt;An active credential can become catastrophic.&lt;/p&gt;
&lt;p&gt;Potential outcomes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repository access&lt;/li&gt;
&lt;li&gt;Cloud infrastructure compromise&lt;/li&gt;
&lt;li&gt;Administrative account takeover&lt;/li&gt;
&lt;li&gt;Database access&lt;/li&gt;
&lt;li&gt;Email service abuse&lt;/li&gt;
&lt;li&gt;Payment system abus…</content:encoded></item><item><title>Build a Minimal Crypto Dashboard with CCXT, CoinGecko and React</title><link>https://jsdev.space/minimal-crypto-dashboard/</link><guid isPermaLink="true">https://jsdev.space/minimal-crypto-dashboard/</guid><description>Build a compact crypto dashboard with live prices, CoinGecko fallback, OHLCV candles, React Query and a monochrome table/chart layout.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In this tutorial, we are going to build a small crypto dashboard that shows live coin prices in a compact table and displays a candlestick chart for the selected coin.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use &lt;a href=&quot;https://github.com/ccxt/ccxt&quot;&gt;CCXT&lt;/a&gt; as the main market data source;&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://www.coingecko.com/&quot;&gt;CoinGecko&lt;/a&gt; as a fallback when the exchange does not have a required pair;&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;fetchOHLCV&lt;/code&gt; to load candlestick data;&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://tanstack.com/query/v3&quot;&gt;React Query&lt;/a&gt; to refresh prices automatically;&lt;/li&gt;
&lt;li&gt;render candles with &lt;a href=&quot;https://github.com/tradingview/lightweight-charts&quot;&gt;Lightweight Charts&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;make the UI clean, compact and monochrome.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the end, we will have a small dashboard where the table sits on the left, the chart sits on the right, and clicking a coin updates the active chart.&lt;/p&gt;
&lt;p&gt;The complete source code for this project is available on &lt;a href=&quot;https://github.com/jsdevspace/Minimal-Crypto-Dashboard&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 1: Create the Project&lt;/h2&gt;
&lt;p&gt;Start with a React + TypeScript Vite project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create vite@latest crypto-dashboard -- --template react-ts
cd crypto-dashboard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the packages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install ccxt express cors @tanstack/react-query lightweight-charts concurrently tsx
npm install -D @types/express @types/cors
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ccxt&lt;/code&gt; for exchange market data;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;express&lt;/code&gt; for the local API;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cors&lt;/code&gt; so the React app can call the API;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@tanstack/react-query&lt;/code&gt; for polling and caching;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lightweight-charts&lt;/code&gt; for the candlestick chart;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;concurrently&lt;/code&gt; to run frontend and backend together;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tsx&lt;/code&gt; to run the TypeScript server directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 2: Add Scripts&lt;/h2&gt;
&lt;p&gt;Update &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;concurrently \&quot;npm run dev:client\&quot; \&quot;npm run dev:server\&quot;&quot;,
    &quot;dev:client&quot;: &quot;vite&quot;,
    &quot;dev:server&quot;: &quot;tsx watch server/index.ts&quot;,
    &quot;build&quot;: &quot;tsc -b &amp;amp;&amp;amp; vite build&quot;,
    &quot;preview&quot;: &quot;vite preview&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now one command starts both the frontend and the backend:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 3: Create the Backend API&lt;/h2&gt;
&lt;p&gt;Create a new file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server/index.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the base Express server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import express from &quot;express&quot;;
import cors from &quot;cors&quot;;
import * as ccxt from &quot;ccxt&quot;;

const app = express();

app.use(cors());

const exchange = new ccxt.bitget({
  enableRateLimit: true,
});

app.listen(4000, () =&amp;gt; {
  console.log(&quot;API running on http://localhost:4000&quot;);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using Bitget here because it works well for public market data and does not require an API key for tickers or candles.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 4: Define the Watchlist&lt;/h2&gt;
&lt;p&gt;Add a small watchlist:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type WatchAsset = {
  symbol: string;
  pair: string;
  geckoId: string;
};

const watchlist: WatchAsset[] = [
  { symbol: &quot;BTC&quot;, pair: &quot;BTC/USDT&quot;, geckoId: &quot;bitcoin&quot; },
  { symbol: &quot;ETH&quot;, pair: &quot;ETH/USDT&quot;, geckoId: &quot;ethereum&quot; },
  { symbol: &quot;SOL&quot;, pair: &quot;SOL/USDT&quot;, geckoId: &quot;solana&quot; },
  { symbol: &quot;DOGE&quot;, pair: &quot;DOGE/USDT&quot;, geckoId: &quot;dogecoin&quot; },
  { symbol: &quot;TAO&quot;, pair: &quot;TAO/USDT&quot;, geckoId: &quot;bittensor&quot; }
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each coin has three fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;symbol&lt;/code&gt; is what we show in the UI;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pair&lt;/code&gt; is what CCXT uses;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;geckoId&lt;/code&gt; is what CoinGecko uses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This small mapping is important because exchanges and CoinGecko do not use the same identifiers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 5: Add CoinGecko Fallback&lt;/h2&gt;
&lt;p&gt;Sometimes a coin exists on CoinGecko but does not have a liquid pair on the exchange we use. Instead of failing, we can fallback to CoinGecko.&lt;/p&gt;
&lt;p&gt;Add this helper:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function getCoinGeckoPrices(ids: string[]) {
  if (ids.length === 0) return {};

  const url = new URL(&quot;https://api.coingecko.com/api/v3/simple/price&quot;);

  url.searchParams.set(&quot;ids&quot;, [...ids, &quot;tether&quot;].join(&quot;,&quot;));
  url.searchParams.set(&quot;vs_currencies&quot;, &quot;usd&quot;);
  url.searchParams.set(&quot;include_24hr_change&quot;, &quot;true&quot;);
  url.searchParams.set(&quot;include_24hr_vol&quot;, &quot;true&quot;);

  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(&quot;CoinGecko request failed&quot;);
  }

  const data = await response.json();
  const tetherUsd = data.tether?.usd ?? 1;

  return Object.fromEntries(
    Object.entries(data).map(([id, value]: any) =&amp;gt; [
      id,
      {
        price: value.usd / tetherUsd,
        change24h: value.usd_24h_change ?? null,
        volume: value.usd_24h_vol ?? null
      }
    ])
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CoinGecko returns prices in USD. Exchange pairs usually come in USDT. For a small dashboard, USD and USDT are often close enough, but we can still normalize CoinGecko values by dividing them by the current Tether price.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 6: Create the Prices Endpoint&lt;/h2&gt;
&lt;p&gt;Now add &lt;code&gt;/api/prices&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&quot;/api/prices&quot;, async (_req, res) =&amp;gt; {
  try {
    await exchange.loadMarkets();

    const pairs = watchlist.map((asset) =&amp;gt; asset.pair);
    const tickers = await exchange.fetchTickers(pairs);

    const missingGeckoIds: string[] = [];

    for (const asset of watchlist) {
      if (!tickers[asset.pair]?.last) {
        missingGeckoIds.push(asset.geckoId);
      }
    }

    const geckoPrices = await getCoinGeckoPrices(missingGeckoIds);

    const prices = watchlist.map((asset) =&amp;gt; {
      const ticker = tickers[asset.pair];
      const gecko = geckoPrices[asset.geckoId] as any;

      return {
        symbol: asset.symbol,
        pair: asset.pair,
        source: ticker?.last ? &quot;ccxt&quot; : &quot;coingecko&quot;,
        price: ticker?.last ?? gecko?.price ?? null,
        high24h: ticker?.high ?? null,
        low24h: ticker?.low ?? null,
        change24h: ticker?.percentage ?? gecko?.change24h ?? null,
        volume: ticker?.quoteVolume ?? gecko?.volume ?? null
      };
    });

    res.json(prices);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: &quot;Failed to load prices&quot; });
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The flow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load exchange markets.&lt;/li&gt;
&lt;li&gt;Request tickers from CCXT.&lt;/li&gt;
&lt;li&gt;Find missing pairs.&lt;/li&gt;
&lt;li&gt;Request missing prices from CoinGecko.&lt;/li&gt;
&lt;li&gt;Merge both sources into one response.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The frontend does not need to know how complicated the data fetching is. It receives one clean array.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 7: Add Candlestick Data with &lt;code&gt;fetchOHLCV&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;For the chart, add another endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&quot;/api/candles/:symbol&quot;, async (req, res) =&amp;gt; {
  try {
    await exchange.loadMarkets();

    const coin = req.params.symbol.toUpperCase();
    const pair = `${coin}/USDT`;

    if (!exchange.markets[pair]) {
      return res.status(404).json({
        error: `No ${pair} market on Bitget`
      });
    }

    const timeframe = String(req.query.timeframe ?? &quot;1m&quot;);
    const limit = Number(req.query.limit ?? 120);

    const candles = await exchange.fetchOHLCV(
      pair,
      timeframe,
      undefined,
      limit
    );

    const result = candles.map(([time, open, high, low, close, volume]) =&amp;gt; ({
      time: Math.floor(time / 1000),
      open,
      high,
      low,
      close,
      volume
    }));

    res.json(result);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: &quot;Failed to load candles&quot; });
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;fetchOHLCV&lt;/code&gt; returns arrays in this format:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
  timestamp,
  open,
  high,
  low,
  close,
  volume
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lightweight Charts expects candle data as objects, so we convert the array into a cleaner structure.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 8: Create Frontend API Helpers&lt;/h2…</content:encoded></item><item><title>Howto Find the Start of a Cycle in a Linked List</title><link>https://jsdev.space/howto/linked-list-cycle-start/</link><guid isPermaLink="true">https://jsdev.space/howto/linked-list-cycle-start/</guid><description>Learn how Floyd’s Fast and Slow Pointer algorithm detects cycles and finds the exact starting node of a loop in O(n) time and O(1) space.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Linked lists are one of the most common data structures used in coding interviews, and cycle detection is one of the most frequently asked problems.&lt;/p&gt;
&lt;p&gt;At first glance, the challenge seems simple: determine whether a linked list contains a loop. However, many interviewers take the problem one step further and ask you to find the exact node where the cycle begins.&lt;/p&gt;
&lt;p&gt;The naive solution is to store every visited node inside a hash set and check whether you&apos;ve seen a node before. While that works, it requires additional memory.&lt;/p&gt;
&lt;p&gt;A much more elegant solution uses Floyd’s Cycle Detection Algorithm, also known as the &lt;strong&gt;Tortoise and Hare Algorithm&lt;/strong&gt;. This approach requires no extra storage and can both detect a cycle and locate its starting point.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Given the head of a singly linked list, return the node where a cycle begins.&lt;/p&gt;
&lt;p&gt;If no cycle exists, return &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input:
3 → 2 → 0 → -4
    ↑       ↓
    └───────┘

Output:
Node with value 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input:
1 → 2
↑   ↓
└───┘

Output:
Node with value 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 3&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input:
1 → null

Output:
null
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why a Normal Traversal Doesn&apos;t Work&lt;/h2&gt;
&lt;p&gt;In a regular linked list, traversal eventually reaches &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let currentNode = head;

while (currentNode) {
  currentNode = currentNode.next;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a cycle exists, the traversal never terminates.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 → 2 → 3 → 4
    ↑       ↓
    └───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pointer keeps moving forever.&lt;/p&gt;
&lt;p&gt;This means we need a smarter approach.&lt;/p&gt;
&lt;h2&gt;Floyd&apos;s Fast and Slow Pointer Technique&lt;/h2&gt;
&lt;p&gt;The core idea is simple.&lt;/p&gt;
&lt;p&gt;Create two pointers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;slowRunner&lt;/code&gt; moves one node at a time&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fastRunner&lt;/code&gt; moves two nodes at a time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the list contains a cycle, the faster pointer will eventually catch the slower pointer.&lt;/p&gt;
&lt;p&gt;If the list has no cycle, the fast pointer will reach the end of the list.&lt;/p&gt;
&lt;h3&gt;Visual Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1 → 2 → 3 → 4 → 5
        ↑       ↓
        └───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iteration 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slowRunner = 2
fastRunner = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iteration 2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slowRunner = 3
fastRunner = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iteration 3:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slowRunner = 4
fastRunner = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pointers meet.&lt;/p&gt;
&lt;p&gt;Once they meet, we know a cycle exists.&lt;/p&gt;
&lt;h2&gt;Finding the Entry Point of the Cycle&lt;/h2&gt;
&lt;p&gt;Detecting the cycle is only half of the problem.&lt;/p&gt;
&lt;p&gt;The next challenge is determining where the loop begins.&lt;/p&gt;
&lt;p&gt;After the first meeting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Leave one pointer at the meeting point.&lt;/li&gt;
&lt;li&gt;Move another pointer back to the head.&lt;/li&gt;
&lt;li&gt;Advance both pointers one step at a time.&lt;/li&gt;
&lt;li&gt;The node where they meet again is the cycle entry.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Why Does This Work?&lt;/h3&gt;
&lt;p&gt;Assume:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a&lt;/code&gt; = distance from head to cycle entry&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt; = cycle length&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c&lt;/code&gt; = distance from cycle entry to the meeting point&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the two pointers meet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slow distance = a + c

fast distance = a + c + k × b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the fast pointer moves twice as quickly:&lt;/p&gt;
&lt;p&gt;2(a+c)=a+c+kb&lt;/p&gt;
&lt;p&gt;Rearranging gives:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = kb - c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means the distance from the head to the cycle entry is exactly equal to the distance from the meeting point back to the cycle entry when moving around the loop.&lt;/p&gt;
&lt;p&gt;As a result, one pointer starting from the head and another starting from the meeting point will arrive at the cycle entry simultaneously.&lt;/p&gt;
&lt;h2&gt;JavaScript Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Definition for singly linked list.
 * function ListNode(value, nextNode) {
 *   this.val = value ?? 0;
 *   this.next = nextNode ?? null;
 * }
 */

/**
 * @param {ListNode} head
 * @returns {ListNode | null}
 */
function findCycleStart(head) {
  let slowRunner = head;
  let fastRunner = head;

  while (fastRunner &amp;amp;&amp;amp; fastRunner.next) {
    slowRunner = slowRunner.next;
    fastRunner = fastRunner.next.next;

    if (slowRunner === fastRunner) {
      let pointerFromHead = head;
      let pointerFromMeeting = slowRunner;

      while (pointerFromHead !== pointerFromMeeting) {
        pointerFromHead = pointerFromHead.next;
        pointerFromMeeting = pointerFromMeeting.next;
      }

      return pointerFromHead;
    }
  }

  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step-by-Step Walkthrough&lt;/h2&gt;
&lt;p&gt;Consider the following linked list:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;3 → 2 → 0 → -4
    ↑       ↓
    └───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Phase 1: Detect the Cycle&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;slowRunner:
3 → 2 → 0 → -4 → 2

fastRunner:
3 → 0 → 2 → -4 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually both pointers meet inside the loop.&lt;/p&gt;
&lt;h3&gt;Phase 2: Locate the Entry&lt;/h3&gt;
&lt;p&gt;Reset one pointer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pointerFromHead = 3
pointerFromMeeting = meeting node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move both one step at a time:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pointerFromHead:
3 → 2

pointerFromMeeting:
0 → -4 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both arrive at node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That node is returned as the cycle entry.&lt;/p&gt;
&lt;h2&gt;Alternative Solution Using a Hash Set&lt;/h2&gt;
&lt;p&gt;Another common approach stores every visited node.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function findCycleStart(head) {
  const visitedNodes = new Set();

  let currentNode = head;

  while (currentNode) {
    if (visitedNodes.has(currentNode)) {
      return currentNode;
    }

    visitedNodes.add(currentNode);
    currentNode = currentNode.next;
  }

  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Easy to understand&lt;/li&gt;
&lt;li&gt;Easy to implement&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Requires additional memory&lt;/li&gt;
&lt;li&gt;Space complexity becomes O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For interview settings, Floyd&apos;s algorithm is generally preferred.&lt;/p&gt;
&lt;h2&gt;Complexity Analysis&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time Complexity&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Space Complexity&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The algorithm performs at most two traversals:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detecting the cycle.&lt;/li&gt;
&lt;li&gt;Finding the cycle entry.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No extra data structures are required.&lt;/p&gt;
&lt;h2&gt;Common Interview Questions&lt;/h2&gt;
&lt;h3&gt;Why do the pointers always meet when a cycle exists?&lt;/h3&gt;
&lt;p&gt;Because the fast pointer gains one node on the slow pointer during each iteration inside the loop. Eventually it catches up.&lt;/p&gt;
&lt;h3&gt;Why reset one pointer to the head?&lt;/h3&gt;
&lt;p&gt;The mathematical relationship between the meeting point and cycle entry guarantees that both pointers will reach the entry node at the same time when moving at equal speed.&lt;/p&gt;
&lt;h3&gt;Can this problem be solved without Floyd&apos;s algorithm?&lt;/h3&gt;
&lt;p&gt;Yes. A hash set can track visited nodes.&lt;/p&gt;
&lt;p&gt;However:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Hash Set:
Time  = O(n)
Space = O(n)

Floyd:
Time  = O(n)
Space = O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Floyd solution is more efficient.&lt;/p&gt;
&lt;h3&gt;What prevents the fast pointer from causing errors?&lt;/h3&gt;
&lt;p&gt;The loop condition ensures safe access:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (fastRunner &amp;amp;&amp;amp; fastRunner.next)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This guarantees that &lt;code&gt;fastRunner.next.next&lt;/code&gt; is only accessed when valid.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Finding the start of a cycle in a linked list is one of the most important linked-list interview problems. The challenge combines pointer manipulation, algorithmic thinking, and a clever mathematical insight.&lt;/p&gt;
&lt;p&gt;The key idea is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a slow pointer moving one step at a time.&lt;/li&gt;
&lt;li&gt;Use a fast pointer moving two steps at a time.&lt;/li&gt;
&lt;li&gt;Detect the meeting point.&lt;/li&gt;
&lt;li&gt;Reset one pointer to the head.&lt;/li&gt;
&lt;li&gt;Move both pointers at the same speed.&lt;/li&gt;
&lt;li&gt;Their next meeting point is the beginning of the cycle.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This elegant technique runs in &lt;strong&gt;O(n)&lt;/strong&gt; time, uses &lt;strong&gt;O(1)&lt;/strong&gt; extra space, and is considered the standard soluti…</content:encoded></item><item><title>How to Build Smooth Scrolling Interfaces with CSS Scroll Snap</title><link>https://jsdev.space/howto/css-scroll-snap-smooth-scrolling/</link><guid isPermaLink="true">https://jsdev.space/howto/css-scroll-snap-smooth-scrolling/</guid><description>Learn how to use CSS Scroll Snap to build smooth carousels, galleries, and full-page scrolling layouts with clean, performant CSS..</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Master native scroll snapping in CSS to create smooth sliders, paginated sections, and polished scrolling experiences without heavy JavaScript libraries.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Modern interfaces rely heavily on scrolling.&lt;/p&gt;
&lt;p&gt;Horizontal product sliders, onboarding screens, fullscreen storytelling pages, mobile galleries, and swipeable dashboards all depend on movement that feels deliberate and predictable. Users expect content to land neatly into place instead of stopping awkwardly between elements.&lt;/p&gt;
&lt;p&gt;For years, developers solved this with JavaScript libraries, custom event listeners, and complicated calculations. That approach works, but it often creates extra maintenance, accessibility problems, and performance issues on mobile devices.&lt;/p&gt;
&lt;p&gt;CSS Scroll Snap gives us a better option.&lt;/p&gt;
&lt;p&gt;Instead of manually calculating scroll positions, you can define snap points directly in CSS and let the browser handle the alignment. The result is cleaner code, better performance, and a smoother user experience.&lt;/p&gt;
&lt;p&gt;In this guide, you will learn how CSS Scroll Snap works, how to configure it properly, and how to use it in real production scenarios such as carousels, galleries, and full-page layouts.&lt;/p&gt;
&lt;h2&gt;What Is CSS Scroll Snap?&lt;/h2&gt;
&lt;p&gt;CSS Scroll Snap is a native CSS feature that controls how scrolling behaves inside a scrollable container.&lt;/p&gt;
&lt;p&gt;You define a scrollable parent, configure the snapping behavior, and then tell child elements where they should align when scrolling stops. Once those rules are in place, the browser automatically moves the scroll position toward the closest snap point.&lt;/p&gt;
&lt;p&gt;Think of it like invisible magnetic points attached to your layout. Instead of the scroll ending in a random position, the content lands exactly where you want it.&lt;/p&gt;
&lt;p&gt;This is useful for interfaces where visual rhythm matters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;horizontal carousels&lt;/li&gt;
&lt;li&gt;image galleries&lt;/li&gt;
&lt;li&gt;onboarding screens&lt;/li&gt;
&lt;li&gt;fullscreen page sections&lt;/li&gt;
&lt;li&gt;mobile navigation layouts&lt;/li&gt;
&lt;li&gt;step-based tutorials&lt;/li&gt;
&lt;li&gt;dashboard panels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best part is that many of these patterns can be created without a slider library.&lt;/p&gt;
&lt;h2&gt;The Basic Structure&lt;/h2&gt;
&lt;p&gt;Every CSS Scroll Snap layout has two main parts.&lt;/p&gt;
&lt;p&gt;First, you need a scroll container. This element must have overflow enabled and a &lt;code&gt;scroll-snap-type&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;Second, you need snap children. These elements usually use &lt;code&gt;scroll-snap-align&lt;/code&gt; to define where they should lock into place.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.snap-container {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.snap-item {
  scroll-snap-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This small amount of CSS already creates a basic horizontal snapping layout.&lt;/p&gt;
&lt;p&gt;The container says: “scroll horizontally and snap strictly.”&lt;/p&gt;
&lt;p&gt;Each child says: “when snapping happens, align me to the center.”&lt;/p&gt;
&lt;h2&gt;Understanding &lt;code&gt;scroll-snap-type&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The most important property is &lt;code&gt;scroll-snap-type&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It tells the browser two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which axis should snap&lt;/li&gt;
&lt;li&gt;how strict snapping should be&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The syntax looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.gallery-track {
  scroll-snap-type: x mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first value controls direction.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;x&lt;/code&gt; for horizontal snapping:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.product-row {
  scroll-snap-type: x mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;y&lt;/code&gt; for vertical snapping:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.page-sections {
  scroll-snap-type: y mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;both&lt;/code&gt; when the container can scroll in both directions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.canvas-grid {
  scroll-snap-type: both proximity;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use logical values such as &lt;code&gt;block&lt;/code&gt; and &lt;code&gt;inline&lt;/code&gt;, which follow the document writing mode. For most everyday layouts, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are easier to understand and maintain.&lt;/p&gt;
&lt;h2&gt;Mandatory vs Proximity Snapping&lt;/h2&gt;
&lt;p&gt;The second value controls how strongly the browser should snap.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;mandatory&lt;/code&gt; value forces the scroll position to land on a snap point.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.carousel {
  scroll-snap-type: x mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful when precision matters. Carousels, onboarding screens, and page-by-page interfaces usually feel better with mandatory snapping because every movement has a clear destination.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;proximity&lt;/code&gt; value is softer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.gallery {
  scroll-snap-type: x proximity;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With proximity snapping, the browser only snaps when the final scroll position is already close to a snap point. This feels more natural for galleries or layouts where users may want a little more freedom.&lt;/p&gt;
&lt;p&gt;A good rule is simple: use &lt;code&gt;mandatory&lt;/code&gt; for strict step-based interfaces and &lt;code&gt;proximity&lt;/code&gt; for exploratory browsing.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;scroll-snap-align&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Once the container knows how to snap, the child elements need to define where they should align.&lt;/p&gt;
&lt;p&gt;That is the job of &lt;code&gt;scroll-snap-align&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.feature-card {
  scroll-snap-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Common values include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;center&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;end&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use &lt;code&gt;start&lt;/code&gt; when each item should align with the beginning of the scroll container.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.chapter {
  scroll-snap-align: start;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is common in full-page vertical layouts where every section should begin at the top of the viewport.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;center&lt;/code&gt; when the item should land in the middle.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.showcase-slide {
  scroll-snap-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works well for carousels, product cards, and image previews.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;end&lt;/code&gt; less often, but it can be helpful for right-aligned panels or special reading layouts.&lt;/p&gt;
&lt;h2&gt;Building a CSS-Only Product Carousel&lt;/h2&gt;
&lt;p&gt;Let’s build a practical carousel using only HTML and CSS.&lt;/p&gt;
&lt;p&gt;This example is intentionally simple, but the structure is close to what you might use in a real interface.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;section class=&quot;product-carousel&quot; aria-label=&quot;Featured products&quot;&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Mechanical Keyboard&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Studio Monitor&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Wireless Mouse&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;USB-C Dock&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Laptop Stand&amp;lt;/article&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add the CSS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.product-carousel {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  padding: 24px;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.product-carousel::-webkit-scrollbar {
  display: none;
}

.product-card {
  flex: 0 0 320px;
  height: 220px;
  display: grid;
  place-items: center;
  border-radius: 18px;
  color: white;
  font-size: 1.4rem;
  font-weight: 700;
  scroll-snap-align: center;
  background: linear-gradient(135deg, #2563eb, #7c3aed);
  box-shadow: 0 16px 40px rgb(15 23 42 / 18%);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you a clean horizontal carousel.&lt;/p&gt;
&lt;p&gt;The container uses &lt;code&gt;overflow-x: auto&lt;/code&gt;, which creates the horizontal scrolling area. The &lt;code&gt;scroll-snap-type: x mandatory&lt;/code&gt; rule tells the browser to snap horizontally. Each card uses &lt;code&gt;scroll-snap-align: center&lt;/code&gt;, so cards land in the middle of the visible area.&lt;/p&gt;
&lt;p&gt;There is no custom JavaScript and no dependency on a carousel package.&lt;/p&gt;
&lt;h2&gt;Improving the Carousel for Real Projects&lt;/h2&gt;
&lt;p&gt;A real carousel often needs better spacing on small screens. You can adjust the card width with &lt;code&gt;clamp()&lt;/code&gt; so the layout adapts naturally.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.product-card {
  flex: 0 0 clamp(240px, 75vw, 360px);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means the card …</content:encoded></item><item><title>Next.js App Router Caching: Why Your Data Stayed Stale</title><link>https://jsdev.space/nextjs-app-cache/</link><guid isPermaLink="true">https://jsdev.space/nextjs-app-cache/</guid><description>Understand how caching actually behaves in the Next.js App Router and learn practical ways to debug force-cache, no-store, and revalidate.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Caching in Next.js usually becomes confusing for the same reason: several different systems can produce almost identical behavior.&lt;/p&gt;
&lt;p&gt;Your API already returns new data.&lt;/p&gt;
&lt;p&gt;You refresh the page.&lt;/p&gt;
&lt;p&gt;The UI still shows an older value.&lt;/p&gt;
&lt;p&gt;You immediately add:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fetch(endpoint, { cache: &quot;no-store&quot; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything starts working.&lt;/p&gt;
&lt;p&gt;Problem solved.&lt;/p&gt;
&lt;p&gt;Until the next question appears:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If every request bypasses caching, why does Next.js ship with an entire caching model in the first place?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The answer is simpler than it first looks.&lt;/p&gt;
&lt;p&gt;In App Router, “caching” is not one feature.&lt;/p&gt;
&lt;p&gt;Different layers can influence what you see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client navigation reuse&lt;/li&gt;
&lt;li&gt;route rendering behavior&lt;/li&gt;
&lt;li&gt;server-side fetch caching&lt;/li&gt;
&lt;li&gt;time-based revalidation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you treat them as one mechanism, debugging quickly becomes frustrating.&lt;/p&gt;
&lt;p&gt;The easiest way to understand caching is not through diagrams or definitions.&lt;/p&gt;
&lt;p&gt;It is through observation.&lt;/p&gt;
&lt;h2&gt;Step One: Detect Whether the Server Rendered Again&lt;/h2&gt;
&lt;p&gt;Before debugging data, verify whether the page actually executed on the server.&lt;/p&gt;
&lt;p&gt;Create a small render indicator.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/components/ServerRenderBadge.tsx

type ServerRenderBadgeProps = {
  name?: string;
};

export function ServerRenderBadge({
  name = &quot;server render&quot;,
}: ServerRenderBadgeProps) {
  const serverRenderedAt = new Date().toISOString();

  return (
    &amp;lt;p className=&quot;text-xs text-slate-500&quot;&amp;gt;
      {name}: &amp;lt;code&amp;gt;{serverRenderedAt}&amp;lt;/code&amp;gt;
    &amp;lt;/p&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component runs on the server.&lt;/p&gt;
&lt;p&gt;Every time the route truly renders again, the timestamp changes.&lt;/p&gt;
&lt;p&gt;Use it inside multiple routes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/catalog/page.tsx

&amp;lt;ServerRenderBadge name=&quot;/catalog render&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// app/catalog/[id]/page.tsx

&amp;lt;ServerRenderBadge name=&quot;/catalog/[id] render&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now perform a simple navigation test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;/catalog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Visit an item page&lt;/li&gt;
&lt;li&gt;Press Back&lt;/li&gt;
&lt;li&gt;Open the same item again&lt;/li&gt;
&lt;li&gt;Reload the browser tab&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You will notice an important distinction.&lt;/p&gt;
&lt;p&gt;Back navigation may preserve the same timestamp.&lt;/p&gt;
&lt;p&gt;Reload often produces a new one.&lt;/p&gt;
&lt;p&gt;That alone reveals something important:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;seeing a page again does not automatically mean the server rendered it again.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Already, you have separated navigation behavior from rendering behavior.&lt;/p&gt;
&lt;h2&gt;Rendering Freshness and Data Freshness Are Different Signals&lt;/h2&gt;
&lt;p&gt;Knowing whether a render happened is useful.&lt;/p&gt;
&lt;p&gt;But most debugging sessions are really about something else:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Did a fresh API response arrive?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Add a second marker inside your data layer.&lt;/p&gt;
&lt;p&gt;Instead of exposing only JSON, return debugging metadata together with the response.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/lib/catalog-api.ts

const CATALOG_API_URL = &quot;https://dummyjson.com&quot;;

type RequestDebugInfo&amp;lt;T&amp;gt; = T &amp;amp; {
  _debug: {
    receivedAt: string;
    cacheTtl?: number;
    validUntil?: string;
  };
};

async function requestCatalog&amp;lt;T&amp;gt;(
  path: string,
  init?: RequestInit &amp;amp; {
    next?: {
      revalidate?: number;
    };
  }
): Promise&amp;lt;RequestDebugInfo&amp;lt;T&amp;gt;&amp;gt; {
  const response = await fetch(
    `${CATALOG_API_URL}${path}`,
    init
  );

  if (!response.ok) {
    throw new Error(
      `Catalog request failed: ${response.status}`
    );
  }

  const receivedAt =
    response.headers.get(&quot;date&quot;) ??
    new Date().toUTCString();

  const payload = await response.json();

  return {
    ...payload,
    _debug: {
      receivedAt,
    },
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Display the metadata alongside the render marker.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div className=&quot;space-y-2 text-sm&quot;&amp;gt;
  &amp;lt;p&amp;gt;
    API response received:
    &amp;lt;code&amp;gt;{item._debug.receivedAt}&amp;lt;/code&amp;gt;
  &amp;lt;/p&amp;gt;

  &amp;lt;ServerRenderBadge
    name=&quot;product page render&quot;
  /&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have two independent measurements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ServerRenderBadge&lt;/strong&gt; → did the route render?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;receivedAt&lt;/strong&gt; → did a new network response arrive?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That distinction changes the entire debugging experience.&lt;/p&gt;
&lt;p&gt;Because a page can absolutely render again while still serving cached data.&lt;/p&gt;
&lt;p&gt;That is usually where the “Next.js is stuck” feeling comes from.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;force-cache&lt;/code&gt;: The UI Updates, the Network Request Doesn&apos;t&lt;/h2&gt;
&lt;p&gt;Start with explicit caching.&lt;/p&gt;
&lt;p&gt;Create a reusable cache profile helper.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/lib/cache-profile.ts

type CacheMode =
  | { strategy: &quot;cached&quot; }
  | { strategy: &quot;live&quot; }
  | {
      strategy: &quot;timed&quot;;
      ttl: number;
    };

function buildFetchPolicy(
  mode: CacheMode
): RequestInit {
  switch (mode.strategy) {
    case &quot;live&quot;:
      return { cache: &quot;no-store&quot; };

    case &quot;timed&quot;:
      return {
        next: {
          revalidate: mode.ttl,
        },
      };

    default:
      return {
        cache: &quot;force-cache&quot;,
      };
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use it in your data loader.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/lib/products.ts

export async function loadProductDetails(
  productId: string
) {
  return requestCatalog(
    `/products/${encodeURIComponent(
      productId
    )}`,
    buildFetchPolicy({
      strategy: &quot;cached&quot;,
    })
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build the application and run production mode.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run build
npm start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now reload the same page several times.&lt;/p&gt;
&lt;p&gt;You may observe something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;product page render:
13:42:18

API response received:
13:37:04
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reload again:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;product page render:
13:42:33

API response received:
13:37:04
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The render changed.&lt;/p&gt;
&lt;p&gt;The response timestamp did not.&lt;/p&gt;
&lt;p&gt;That tells you exactly what happened.&lt;/p&gt;
&lt;p&gt;The route executed again.&lt;/p&gt;
&lt;p&gt;The fetch layer reused cached data.&lt;/p&gt;
&lt;p&gt;This is not broken behavior.&lt;/p&gt;
&lt;p&gt;It is precisely what &lt;code&gt;force-cache&lt;/code&gt; is designed to do.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;no-store&lt;/code&gt;: When Every Request Must Be Fresh&lt;/h2&gt;
&lt;p&gt;Some pages cannot tolerate stale data.&lt;/p&gt;
&lt;p&gt;Switch the loader to a live profile.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function loadProductDetails(
  productId: string
) {
  return requestCatalog(
    `/products/${productId}`,
    buildFetchPolicy({
      strategy: &quot;live&quot;,
    })
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test reload behavior again.&lt;/p&gt;
&lt;p&gt;Now the signals move together.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render:
14:02:11

received:
14:02:11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render:
14:02:19

received:
14:02:19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every request performs a new fetch.&lt;/p&gt;
&lt;p&gt;This is useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;account balances&lt;/li&gt;
&lt;li&gt;order states&lt;/li&gt;
&lt;li&gt;admin dashboards&lt;/li&gt;
&lt;li&gt;user-specific operational data&lt;/li&gt;
&lt;li&gt;real-time internal tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But freshness comes with a cost.&lt;/p&gt;
&lt;p&gt;More requests.&lt;/p&gt;
&lt;p&gt;More external API pressure.&lt;/p&gt;
&lt;p&gt;Lower reuse efficiency.&lt;/p&gt;
&lt;p&gt;For many public pages, this is unnecessary overkill.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;revalidate&lt;/code&gt;: Usually the Practical Choice&lt;/h2&gt;
&lt;p&gt;Most applications need something between permanent reuse and constant refetching.&lt;/p&gt;
&lt;p&gt;That is where timed revalidation becomes valuable.&lt;/p&gt;
&lt;p&gt;Extend the response metadata.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function requestCatalog&amp;lt;T&amp;gt;(
  path: string,
  init?: RequestInit &amp;amp; {
    next?: {
      revalidate?: number;
    };
  }
) {
  const response = await fetch(
    `${CATALOG_API_URL}${path}`,
    init
  );

  const receivedAt =
    response.headers.get(&quot;date&quot;) ??
    new Date().toUTCString();

  const ttl =
    init?.next?.revalidate;

  const validUntil =
    typeof ttl === &quot;number&quot;
      ? new Date(
          Date.parse(receivedAt) +
            ttl * 1000
        ).toUTCString()
      : undefined;

  const data = await response.json();

  return {
    ...data,
    _d…</content:encoded></item><item><title>Protecting Next.js Applications in the Era of Server Actions</title><link>https://jsdev.space/server-actions-security/</link><guid isPermaLink="true">https://jsdev.space/server-actions-security/</guid><description>Modern Next.js apps need more than JWTs and middleware. Learn practical security patterns for Server Actions, React 19, Zod, RBAC, CSP, and data protection.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;The New Frontend Security Model&lt;/h2&gt;
&lt;p&gt;Frontend security used to feel relatively straightforward.&lt;/p&gt;
&lt;p&gt;A few years ago, the architecture looked familiar.&lt;/p&gt;
&lt;p&gt;You had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a SPA running in the browser&lt;/li&gt;
&lt;li&gt;a backend API somewhere else&lt;/li&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;CORS policies&lt;/li&gt;
&lt;li&gt;REST endpoints&lt;/li&gt;
&lt;li&gt;client-side state management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The boundary was obvious.&lt;/p&gt;
&lt;p&gt;Frontend lived here.&lt;/p&gt;
&lt;p&gt;Backend lived there.&lt;/p&gt;
&lt;p&gt;Security responsibilities were reasonably separated.&lt;/p&gt;
&lt;p&gt;Modern React applications no longer operate in that world.&lt;/p&gt;
&lt;p&gt;The arrival of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React Server Components&lt;/li&gt;
&lt;li&gt;Server Actions&lt;/li&gt;
&lt;li&gt;hybrid rendering&lt;/li&gt;
&lt;li&gt;Next.js App Router&lt;/li&gt;
&lt;li&gt;server-side data access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;has fundamentally changed how frontend systems behave.&lt;/p&gt;
&lt;p&gt;The application boundary has become softer.&lt;/p&gt;
&lt;p&gt;The frontend now executes logic on the server.&lt;/p&gt;
&lt;p&gt;The server now lives surprisingly close to the UI layer.&lt;/p&gt;
&lt;p&gt;And with that architectural shift comes an uncomfortable reality:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;many traditional frontend security assumptions are no longer sufficient.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The issue is not that Next.js is insecure.&lt;/p&gt;
&lt;p&gt;The issue is that developers frequently apply old security mental models to a new execution model.&lt;/p&gt;
&lt;h2&gt;Why Server Actions Quietly Changed the Threat Model&lt;/h2&gt;
&lt;p&gt;Server Actions are one of the most powerful additions to modern React development.&lt;/p&gt;
&lt;p&gt;They are also one of the easiest places to accidentally introduce vulnerabilities.&lt;/p&gt;
&lt;p&gt;At first glance, Server Actions feel magical.&lt;/p&gt;
&lt;p&gt;You write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use server&quot;;

export async function updateProfile() {
  // server logic
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then call it directly from a form or component.&lt;/p&gt;
&lt;p&gt;No REST handler.&lt;/p&gt;
&lt;p&gt;No API folder.&lt;/p&gt;
&lt;p&gt;No explicit endpoint wiring.&lt;/p&gt;
&lt;p&gt;Extremely convenient.&lt;/p&gt;
&lt;p&gt;But convenience hides architecture.&lt;/p&gt;
&lt;p&gt;Every Server Action is effectively:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;a remotely callable server endpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That matters.&lt;/p&gt;
&lt;p&gt;Because many developers mentally treat Server Actions like private helper functions.&lt;/p&gt;
&lt;p&gt;They are not.&lt;/p&gt;
&lt;p&gt;They are part of your application&apos;s public execution surface.&lt;/p&gt;
&lt;h2&gt;The Dangerous Illusion of Hidden Server Logic&lt;/h2&gt;
&lt;p&gt;A common misunderstanding looks like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Users can only trigger this action from my form.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not necessarily.&lt;/p&gt;
&lt;p&gt;Attackers do not care about your component hierarchy.&lt;/p&gt;
&lt;p&gt;They care about callable interfaces.&lt;/p&gt;
&lt;p&gt;If an action exists and can be reached, assumptions based on UI restrictions become unreliable.&lt;/p&gt;
&lt;p&gt;Consider this example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use server&quot;;

export async function promoteUser(payload: any) {
  await db.user.update({
    where: {
      id: payload.id,
    },

    data: {
      role: payload.role,
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code seems small.&lt;/p&gt;
&lt;p&gt;Clean.&lt;/p&gt;
&lt;p&gt;Fast to ship.&lt;/p&gt;
&lt;p&gt;And deeply problematic.&lt;/p&gt;
&lt;p&gt;Problems immediately appear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;any input&lt;/li&gt;
&lt;li&gt;no session validation&lt;/li&gt;
&lt;li&gt;no authorization&lt;/li&gt;
&lt;li&gt;unrestricted role updates&lt;/li&gt;
&lt;li&gt;no ownership checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An attacker does not need your UI.&lt;/p&gt;
&lt;p&gt;They only need a way to invoke the action.&lt;/p&gt;
&lt;h2&gt;Treat Server Actions Like Production API Endpoints&lt;/h2&gt;
&lt;p&gt;The safer mental model is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;every Server Action should be treated like a hardened API endpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;validate input&lt;/li&gt;
&lt;li&gt;authenticate request context&lt;/li&gt;
&lt;li&gt;authorize resource access&lt;/li&gt;
&lt;li&gt;restrict writable fields&lt;/li&gt;
&lt;li&gt;control returned data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A stronger implementation might look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use server&quot;;

import { z } from &quot;zod&quot;;
import { getSession } from &quot;@/lib/auth&quot;;

const schema = z.object({
  userId: z.string().uuid(),
  displayName: z.string().min(2).max(80),
});

export async function updateProfile(
  rawInput: unknown
) {

  const session =
    await getSession();

  if (!session) {
    throw new Error(
      &quot;Unauthorized&quot;
    );
  }

  const input =
    schema.parse(rawInput);

  if (
    session.user.id !==
    input.userId
  ) {

    throw new Error(
      &quot;Forbidden&quot;
    );
  }

  return db.user.update({

    where: {
      id: input.userId,
    },

    data: {
      displayName:
        input.displayName,
    },

    select: {
      id: true,
      displayName: true,
      avatar: true,
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Several things changed.&lt;/p&gt;
&lt;p&gt;Input became validated.&lt;/p&gt;
&lt;p&gt;Identity became explicit.&lt;/p&gt;
&lt;p&gt;Authorization became enforced.&lt;/p&gt;
&lt;p&gt;Response leakage became restricted.&lt;/p&gt;
&lt;p&gt;That difference matters far more than syntax style.&lt;/p&gt;
&lt;h2&gt;TypeScript Is Not Runtime Security&lt;/h2&gt;
&lt;p&gt;Many frontend teams heavily rely on TypeScript.&lt;/p&gt;
&lt;p&gt;That is good.&lt;/p&gt;
&lt;p&gt;TypeScript improves developer ergonomics dramatically.&lt;/p&gt;
&lt;p&gt;But TypeScript alone does not secure Server Actions. Read &lt;a href=&quot;https://jsdev.space/typescript-security-guide/&quot;&gt;TypeScript Is Not a Security Boundary&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Because TypeScript disappears after compilation.&lt;/p&gt;
&lt;p&gt;Production JavaScript does not preserve your compile-time guarantees.&lt;/p&gt;
&lt;p&gt;If an attacker sends:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;age&quot;: &quot;DROP TABLE users&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript will not intervene.&lt;/p&gt;
&lt;p&gt;The compiler is already gone.&lt;/p&gt;
&lt;p&gt;This is where &lt;strong&gt;runtime validation&lt;/strong&gt; becomes critical.&lt;/p&gt;
&lt;h3&gt;Defense in Depth with Zod&lt;/h3&gt;
&lt;p&gt;A stronger strategy combines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compile-time guarantees&lt;/li&gt;
&lt;li&gt;runtime validation&lt;/li&gt;
&lt;li&gt;explicit business checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zod fits naturally into modern Next.js workflows.&lt;/p&gt;
&lt;p&gt;Define schemas once.&lt;/p&gt;
&lt;p&gt;Use them for validation and type inference.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;;

export const accountSchema =
  z.object({

    email:
      z.email(),

    username:
      z.string()
        .min(3)
        .max(32),

    age:
      z.number()
        .int()
        .min(13)
        .optional(),
  });

type AccountInput = z.infer&amp;lt;typeof accountSchema&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;runtime validation exists&lt;/li&gt;
&lt;li&gt;types remain synchronized&lt;/li&gt;
&lt;li&gt;schemas become self-documenting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That combination creates a meaningful defense layer.&lt;/p&gt;
&lt;p&gt;Not complete security.&lt;/p&gt;
&lt;p&gt;But stronger guarantees.&lt;/p&gt;
&lt;h3&gt;Making Server Actions Safer with &lt;code&gt;next-safe-action&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Once teams begin heavily using Server Actions, repetitive validation logic starts appearing everywhere.&lt;/p&gt;
&lt;p&gt;Typical pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;validate input
check session
parse schema
handle errors
return typed response
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Repeated dozens of times.&lt;/p&gt;
&lt;p&gt;This is exactly the problem &lt;a href=&quot;https://next-safe-action.dev/&quot;&gt;next-safe-action&lt;/a&gt; tries to solve.&lt;/p&gt;
&lt;p&gt;Instead of manually building wrappers for every action, you define a secure action client once.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { createSafeActionClient } from &quot;next-safe-action&quot;;

export const actionClient = createSafeActionClient();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then define strongly validated actions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;;

const schema = z.object({
  id: z.string().uuid(),
  name: z.string().min(2),
});

export const renameProject = actionClient.schema(schema)
  .action(
    async ({
      parsedInput,
    }) =&amp;gt; {
      return db.project.update({
        where: {
          id:
            parsedInput.id,
        },
        data: {
          name:
            parsedInput.name,
        },
      });
    }
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;validation becomes standardized&lt;/li&gt;
&lt;li&gt;parsing becomes automatic&lt;/li&gt;
&lt;li&gt;typing stays synchronized&lt;/li&gt;
&lt;li&gt;failure handling improves&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This reduces boilerplate while preserving security boundaries.&lt;/p&gt;
&lt;h2&gt;Server Components and Accidental Data Leaks&lt;/h2&gt;
&lt;p&gt;One of the most subtle problems in modern Next.js architecture is server-to-client leakage.&lt;/p&gt;
&lt;p&gt;Traditional SPAs had a relativ…</content:encoded></item><item><title>Howto Use Git Bisect to Find the Commit That Broke Everything</title><link>https://jsdev.space/howto/git-bisect/</link><guid isPermaLink="true">https://jsdev.space/howto/git-bisect/</guid><description>Something worked last week and now it’s broken. The repository has fifty new commits and nobody knows what changed.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every developer eventually runs into the same unpleasant scenario.&lt;/p&gt;
&lt;p&gt;The application worked perfectly a week ago.&lt;/p&gt;
&lt;p&gt;Now something is broken.&lt;/p&gt;
&lt;p&gt;Tests fail.&lt;/p&gt;
&lt;p&gt;The UI behaves strangely.&lt;/p&gt;
&lt;p&gt;An API endpoint returns nonsense.&lt;/p&gt;
&lt;p&gt;And during that week?&lt;/p&gt;
&lt;p&gt;Forty commits landed from multiple developers.&lt;/p&gt;
&lt;p&gt;Maybe eighty.&lt;/p&gt;
&lt;p&gt;Maybe three hundred.&lt;/p&gt;
&lt;p&gt;You could inspect commits one by one.&lt;/p&gt;
&lt;p&gt;Open diffs.&lt;/p&gt;
&lt;p&gt;Run builds.&lt;/p&gt;
&lt;p&gt;Test manually.&lt;/p&gt;
&lt;p&gt;Slowly lose your patience.&lt;/p&gt;
&lt;p&gt;Or you could let Git do the hard work.&lt;/p&gt;
&lt;p&gt;That’s exactly what git bisect exists for.&lt;/p&gt;
&lt;p&gt;Instead of checking commits sequentially, Git performs a binary search through commit history.&lt;/p&gt;
&lt;p&gt;You tell Git:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this version works&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this version is broken&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git then jumps directly to the middle of the range and asks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does it work here?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After several answers, the offending commit appears.&lt;/p&gt;
&lt;p&gt;Forty commits?&lt;/p&gt;
&lt;p&gt;Roughly six checks.&lt;/p&gt;
&lt;p&gt;Eight hundred commits?&lt;/p&gt;
&lt;p&gt;Around ten.&lt;/p&gt;
&lt;p&gt;That’s the power of binary search applied to version control.&lt;/p&gt;
&lt;h2&gt;The Core Idea: Binary Search for Broken Code&lt;/h2&gt;
&lt;p&gt;If you&apos;ve ever searched through a sorted array using binary search, the logic will feel familiar.&lt;/p&gt;
&lt;p&gt;Suppose you have twelve commits:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;c0&lt;/code&gt; worked&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c11&lt;/code&gt; is broken&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of checking:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c1 → c2 → c3 → c4 → c5 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git immediately chooses the midpoint.&lt;/p&gt;
&lt;p&gt;Something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You test it.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;c5&lt;/code&gt; works:&lt;/p&gt;
&lt;p&gt;the bad commit must be later.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;c5&lt;/code&gt; fails:&lt;/p&gt;
&lt;p&gt;the problem must be earlier.&lt;/p&gt;
&lt;p&gt;Each answer cuts the search space in half.&lt;/p&gt;
&lt;p&gt;That’s why &lt;code&gt;git bisect&lt;/code&gt; becomes incredibly efficient on large histories.&lt;/p&gt;
&lt;h2&gt;Starting a Git Bisect Session&lt;/h2&gt;
&lt;p&gt;Begin by starting a bisect session.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now tell Git:&lt;/p&gt;
&lt;p&gt;the current state is broken.&lt;/p&gt;
&lt;p&gt;Usually this means your current &lt;code&gt;HEAD&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, mark a known working commit.&lt;/p&gt;
&lt;p&gt;You can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a commit hash&lt;/li&gt;
&lt;li&gt;a branch reference&lt;/li&gt;
&lt;li&gt;a tag&lt;/li&gt;
&lt;li&gt;an older release version&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect good v1.2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, Git calculates the midpoint automatically and checks out a commit somewhere between those two states.&lt;/p&gt;
&lt;p&gt;You’ll see something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Bisecting: 4 revisions left to test
Checking out commit abc1234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your job is simple:&lt;/p&gt;
&lt;p&gt;test the application.&lt;/p&gt;
&lt;h2&gt;Testing Each Candidate Commit&lt;/h2&gt;
&lt;p&gt;Git has moved you to a candidate commit.&lt;/p&gt;
&lt;p&gt;Now answer one question:&lt;/p&gt;
&lt;p&gt;does the bug exist here?&lt;/p&gt;
&lt;p&gt;If everything works:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect good
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the bug is present:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git immediately narrows the search range and jumps again.&lt;/p&gt;
&lt;p&gt;You repeat the cycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;test&lt;/li&gt;
&lt;li&gt;mark good or bad&lt;/li&gt;
&lt;li&gt;test again&lt;/li&gt;
&lt;li&gt;mark again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventually Git reaches the answer.&lt;/p&gt;
&lt;h2&gt;When Git Finds the Guilty Commit&lt;/h2&gt;
&lt;p&gt;After several rounds, Git stops and reports the result.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a3f8c12 is the first bad commit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That commit introduced the problem.&lt;/p&gt;
&lt;p&gt;You now know exactly where to investigate.&lt;/p&gt;
&lt;p&gt;Instead of debugging forty random changes, you&apos;re examining one specific diff.&lt;/p&gt;
&lt;p&gt;That alone can save hours.&lt;/p&gt;
&lt;h2&gt;A Practical Example&lt;/h2&gt;
&lt;p&gt;Imagine this timeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A week ago:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything worked.&lt;/p&gt;
&lt;p&gt;Today:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Production is broken.&lt;/p&gt;
&lt;p&gt;Start the session:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect start
git bisect bad
git bisect good c0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git chooses the midpoint.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Checking out c5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You run the app.&lt;/p&gt;
&lt;p&gt;Suppose:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c5&lt;/code&gt; still works.&lt;/p&gt;
&lt;p&gt;Mark it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect good
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git shrinks the search window.&lt;/p&gt;
&lt;p&gt;Now it may jump to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You test again.&lt;/p&gt;
&lt;p&gt;Suppose:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c8&lt;/code&gt; fails.&lt;/p&gt;
&lt;p&gt;Mark it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Search space shrinks again.&lt;/p&gt;
&lt;p&gt;After a few iterations:&lt;/p&gt;
&lt;p&gt;Git identifies the exact commit responsible.&lt;/p&gt;
&lt;p&gt;You never manually walked through the entire history.&lt;/p&gt;
&lt;h2&gt;Don’t Forget to Reset Afterwards&lt;/h2&gt;
&lt;p&gt;This is easy to forget.&lt;/p&gt;
&lt;p&gt;During a bisect session, Git repeatedly checks out temporary commits.&lt;/p&gt;
&lt;p&gt;Once you’re finished, return to your original branch state:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without reset, you may wonder why your repository suddenly sits on some detached historical commit.&lt;/p&gt;
&lt;p&gt;Always reset when you&apos;re done.&lt;/p&gt;
&lt;h2&gt;Automating Git Bisect with Tests&lt;/h2&gt;
&lt;p&gt;Manual testing works.&lt;/p&gt;
&lt;p&gt;Automated testing works even better.&lt;/p&gt;
&lt;p&gt;If you already have a command that verifies correctness and returns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0 → success
non-zero → failure
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can automate the entire search.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect start
git bisect bad HEAD
git bisect good v1.2.0
git bisect run npm test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Git handles everything.&lt;/p&gt;
&lt;p&gt;For each candidate commit it will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;checkout commit&lt;/li&gt;
&lt;li&gt;run tests&lt;/li&gt;
&lt;li&gt;interpret result&lt;/li&gt;
&lt;li&gt;continue searching&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No manual interaction required.&lt;/p&gt;
&lt;p&gt;This becomes incredibly powerful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;failing unit tests&lt;/li&gt;
&lt;li&gt;broken builds&lt;/li&gt;
&lt;li&gt;regression bugs&lt;/li&gt;
&lt;li&gt;CI debugging&lt;/li&gt;
&lt;li&gt;flaky integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using Custom Validation Scripts&lt;/h2&gt;
&lt;p&gt;You aren&apos;t limited to npm test.&lt;/p&gt;
&lt;p&gt;Any script can drive bisect.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect run ./check-build.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect run pnpm lint
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or a custom Node validation script:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect run node verify-api.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As long as the command returns a proper exit code, Git can automate the search.&lt;/p&gt;
&lt;p&gt;That flexibility makes &lt;code&gt;git bisect&lt;/code&gt; useful far beyond traditional testing.&lt;/p&gt;
&lt;h2&gt;Why Developers Forget This Tool Exists&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is oddly underused.&lt;/p&gt;
&lt;p&gt;Many developers know it exists but rarely reach for it.&lt;/p&gt;
&lt;p&gt;Instead they default to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reading commit history manually&lt;/li&gt;
&lt;li&gt;guessing likely changes&lt;/li&gt;
&lt;li&gt;debugging recent merges&lt;/li&gt;
&lt;li&gt;blaming dependencies&lt;/li&gt;
&lt;li&gt;reopening old PRs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes that works.&lt;/p&gt;
&lt;p&gt;Often it wastes time.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; gives you something much stronger:&lt;/p&gt;
&lt;p&gt;a systematic search strategy.&lt;/p&gt;
&lt;p&gt;No guessing.&lt;/p&gt;
&lt;p&gt;No intuition games.&lt;/p&gt;
&lt;p&gt;Just elimination.&lt;/p&gt;
&lt;h2&gt;When Git Bisect Becomes Especially Valuable&lt;/h2&gt;
&lt;p&gt;This tool shines in situations like:&lt;/p&gt;
&lt;h3&gt;Regression Bugs&lt;/h3&gt;
&lt;p&gt;Something used to work.&lt;/p&gt;
&lt;p&gt;Now it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;Classic bisect territory.&lt;/p&gt;
&lt;h3&gt;Large Teams&lt;/h3&gt;
&lt;p&gt;Many developers.&lt;/p&gt;
&lt;p&gt;Many commits.&lt;/p&gt;
&lt;p&gt;Unclear ownership.&lt;/p&gt;
&lt;p&gt;Bisect dramatically reduces noise.&lt;/p&gt;
&lt;h3&gt;Old Codebases&lt;/h3&gt;
&lt;p&gt;You don&apos;t remember when the behavior changed.&lt;/p&gt;
&lt;p&gt;The repository history does.&lt;/p&gt;
&lt;h3&gt;CI Failures&lt;/h3&gt;
&lt;p&gt;A build started failing somewhere in the last two weeks.&lt;/p&gt;
&lt;p&gt;Nobody knows where.&lt;/p&gt;
&lt;p&gt;Bisect knows.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is essentially binary search applied to commit history.&lt;/p&gt;
&lt;p&gt;That simple idea makes it incredibly effective.&lt;/p&gt;
&lt;p&gt;Instead of checking commits one after another, Git repeatedly cuts the search range in half.&lt;/p&gt;
&lt;p&gt;Forty commits?&lt;…</content:encoded></item><item><title>TypeScript Security From Backend to Browser</title><link>https://jsdev.space/typescript-security-guide/</link><guid isPermaLink="true">https://jsdev.space/typescript-security-guide/</guid><description>A deep practical guide to securing TypeScript applications across Node.js, React, Next.js, APIs, databases, SSR, and modern web infrastructure.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;TypeScript Is Not a Security Boundary&lt;/h2&gt;
&lt;p&gt;TypeScript has quietly become the default language of modern JavaScript engineering.&lt;/p&gt;
&lt;p&gt;React applications.&lt;/p&gt;
&lt;p&gt;Next.js platforms.&lt;/p&gt;
&lt;p&gt;NestJS APIs.&lt;/p&gt;
&lt;p&gt;GraphQL services.&lt;/p&gt;
&lt;p&gt;CLI tooling.&lt;/p&gt;
&lt;p&gt;Infrastructure scripts.&lt;/p&gt;
&lt;p&gt;Entire SaaS companies run on TypeScript stacks.&lt;/p&gt;
&lt;p&gt;And somewhere along the way, many developers start developing a subtle — and understandable — misconception:&lt;/p&gt;
&lt;p&gt;“We use TypeScript, so a large chunk of security problems are probably handled already.”&lt;/p&gt;
&lt;p&gt;Not explicitly.&lt;/p&gt;
&lt;p&gt;Not consciously.&lt;/p&gt;
&lt;p&gt;But indirectly.&lt;/p&gt;
&lt;p&gt;Strong types feel safe.&lt;/p&gt;
&lt;p&gt;Strict mode feels safe.&lt;/p&gt;
&lt;p&gt;Interfaces feel safe.&lt;/p&gt;
&lt;p&gt;DTOs, generics, discriminated unions, branded types — they create an environment that looks disciplined.&lt;/p&gt;
&lt;p&gt;And that discipline genuinely reduces bugs.&lt;/p&gt;
&lt;p&gt;But security vulnerabilities live in a different universe.&lt;/p&gt;
&lt;p&gt;A SQL injection payload is still a valid string.&lt;/p&gt;
&lt;p&gt;An XSS payload is still a valid string.&lt;/p&gt;
&lt;p&gt;A malicious JWT is still a valid string.&lt;/p&gt;
&lt;p&gt;A poisoned npm dependency still compiles perfectly.&lt;/p&gt;
&lt;p&gt;The TypeScript compiler is not failing.&lt;/p&gt;
&lt;p&gt;It is doing exactly what it was designed to do.&lt;/p&gt;
&lt;p&gt;This distinction matters.&lt;/p&gt;
&lt;p&gt;Because many production incidents happen precisely at the border where developers confuse type safety with security guarantees.&lt;/p&gt;
&lt;p&gt;In this article, we&apos;ll walk through the major security concerns affecting modern TypeScript applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;backend attacks&lt;/li&gt;
&lt;li&gt;database injections&lt;/li&gt;
&lt;li&gt;SSR vulnerabilities&lt;/li&gt;
&lt;li&gt;React and browser security&lt;/li&gt;
&lt;li&gt;JWT mistakes&lt;/li&gt;
&lt;li&gt;prototype pollution&lt;/li&gt;
&lt;li&gt;GraphQL abuse&lt;/li&gt;
&lt;li&gt;SSRF&lt;/li&gt;
&lt;li&gt;supply-chain attacks&lt;/li&gt;
&lt;li&gt;runtime validation&lt;/li&gt;
&lt;li&gt;modern tooling strategies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And most importantly:&lt;/p&gt;
&lt;p&gt;how to build a security model that works with TypeScript instead of expecting TypeScript to do security&apos;s job.&lt;/p&gt;
&lt;h2&gt;Backend Reality: HTTP Requests Arrive Before Type&lt;/h2&gt;
&lt;p&gt;TypeScript creates contracts between layers.&lt;/p&gt;
&lt;p&gt;Controllers expect DTOs.&lt;/p&gt;
&lt;p&gt;Services expect interfaces.&lt;/p&gt;
&lt;p&gt;Repositories expect known shapes.&lt;/p&gt;
&lt;p&gt;The internet ignores all of them.&lt;/p&gt;
&lt;p&gt;Your backend does not receive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type LoginRequest = {
  email: string;
  password: string;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It receives:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;raw bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those bytes might become JSON.&lt;/p&gt;
&lt;p&gt;Or malformed JSON.&lt;/p&gt;
&lt;p&gt;Or intentionally malicious JSON.&lt;/p&gt;
&lt;p&gt;Or oversized payloads.&lt;/p&gt;
&lt;p&gt;Or nested objects designed to break assumptions.&lt;/p&gt;
&lt;p&gt;Before your beautiful TypeScript types even exist, untrusted data already crossed your trust boundary.&lt;/p&gt;
&lt;p&gt;That is why runtime validation matters.&lt;/p&gt;
&lt;p&gt;Not optional validation.&lt;/p&gt;
&lt;p&gt;Not “we validate most routes.”&lt;/p&gt;
&lt;p&gt;Boundary validation.&lt;/p&gt;
&lt;p&gt;Every request.&lt;/p&gt;
&lt;p&gt;Every external payload.&lt;/p&gt;
&lt;p&gt;Every webhook.&lt;/p&gt;
&lt;p&gt;Every environment variable.&lt;/p&gt;
&lt;p&gt;Every file import.&lt;/p&gt;
&lt;p&gt;Without this mindset, types become documentation — not protection.&lt;/p&gt;
&lt;h2&gt;SQL Injection Still Exists Inside ORM Code&lt;/h2&gt;
&lt;p&gt;Many developers associate SQL injection with early PHP tutorials.&lt;/p&gt;
&lt;p&gt;String concatenation.&lt;/p&gt;
&lt;p&gt;Handwritten SQL.&lt;/p&gt;
&lt;p&gt;Obvious mistakes.&lt;/p&gt;
&lt;p&gt;Modern ORMs supposedly solved that problem.&lt;/p&gt;
&lt;p&gt;Mostly true.&lt;/p&gt;
&lt;p&gt;Mostly.&lt;/p&gt;
&lt;p&gt;Because ORMs reduce risk until developers partially bypass them.&lt;/p&gt;
&lt;p&gt;Consider this seemingly harmless endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&quot;/users&quot;, async (req, res) =&amp;gt; {
  const { sortColumn, order } = req.query;

  const users = await connection.query(
    `SELECT * FROM users
     ORDER BY ${sortColumn} ${order}`
  );

  res.json(users);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From TypeScript&apos;s perspective:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sortColumn: string
order: string
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything typechecks.&lt;/p&gt;
&lt;p&gt;The database sees something different.&lt;/p&gt;
&lt;p&gt;Suppose an attacker sends:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/users?sortColumn=name&amp;amp;order=ASC;DROP TABLE users;--
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The database does not care that your IDE showed no errors.&lt;/p&gt;
&lt;p&gt;The query parser now receives executable SQL.&lt;/p&gt;
&lt;p&gt;This is where many teams misunderstand what ORMs actually guarantee.&lt;/p&gt;
&lt;p&gt;An ORM reduces accidental SQL construction.&lt;/p&gt;
&lt;p&gt;It does not automatically sanitize arbitrary string interpolation.&lt;/p&gt;
&lt;p&gt;Safer approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allowedColumns = [
  &quot;name&quot;,
  &quot;email&quot;,
  &quot;createdAt&quot;
] as const;

const allowedDirections = [
  &quot;ASC&quot;,
  &quot;DESC&quot;
] as const;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Validate.&lt;/p&gt;
&lt;p&gt;Allowlist.&lt;/p&gt;
&lt;p&gt;Then build controlled queries.&lt;/p&gt;
&lt;p&gt;Or better:&lt;/p&gt;
&lt;p&gt;use query APIs that parameterize values automatically.&lt;/p&gt;
&lt;h2&gt;The Hidden ORM Trap: &lt;code&gt;QueryBuilder&lt;/code&gt; and Raw()&lt;/h2&gt;
&lt;p&gt;This is where real-world codebases become interesting.&lt;/p&gt;
&lt;p&gt;Developers often say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“We already use QueryBuilder.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Good.&lt;/p&gt;
&lt;p&gt;That alone changes nothing.&lt;/p&gt;
&lt;p&gt;Unsafe &lt;code&gt;QueryBuilder&lt;/code&gt; code still exists everywhere.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const qb = userRepository.createQueryBuilder(&quot;user&quot;);

qb.where(
  `user.role=&apos;${filter}&apos;`
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks sophisticated.&lt;/p&gt;
&lt;p&gt;Still injection-prone.&lt;/p&gt;
&lt;p&gt;Same vulnerability.&lt;/p&gt;
&lt;p&gt;New abstraction layer.&lt;/p&gt;
&lt;p&gt;TypeORM&apos;s &lt;code&gt;Raw()&lt;/code&gt; helper is another common footgun.&lt;/p&gt;
&lt;p&gt;Unsafe:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;where: {
  name: Raw(
    alias =&amp;gt; `${alias} LIKE &apos;%${query}%&apos;`
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, user input becomes part of executable SQL again.&lt;/p&gt;
&lt;p&gt;Safe alternative:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;where: {
  name: Raw(
    alias =&amp;gt; `${alias} ILIKE :query`,
    {
      query: `%${query}%`
    }
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parameterized placeholders exist for a reason.&lt;/p&gt;
&lt;p&gt;Use them.&lt;/p&gt;
&lt;h2&gt;NoSQL Injection Is Still Injection&lt;/h2&gt;
&lt;p&gt;MongoDB developers sometimes assume they escaped SQL problems entirely.&lt;/p&gt;
&lt;p&gt;Not quite.&lt;/p&gt;
&lt;p&gt;You traded syntax.&lt;/p&gt;
&lt;p&gt;Not threat models.&lt;/p&gt;
&lt;p&gt;Classic vulnerable login handler:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.post(&quot;/login&quot;, async (req, res) =&amp;gt; {
    const { username,password } = req.body;
    const user =
      await User.findOne({
        username,
        password
      });
    if (user) {
      return res.sendStatus(200);
    }
    res.sendStatus(401);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine this payload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;username&quot;: {
    &quot;$ne&quot;: null
  },
  &quot;password&quot;: {
    &quot;$ne&quot;: null
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting query becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  username: {
    $ne: null
  },

  password: {
    $ne: null
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congratulations.&lt;/p&gt;
&lt;p&gt;You may have just authenticated the first matching user.&lt;/p&gt;
&lt;p&gt;The compiler did not fail.&lt;/p&gt;
&lt;p&gt;Because objects are valid objects.&lt;/p&gt;
&lt;p&gt;Runtime validation solves this.&lt;/p&gt;
&lt;p&gt;Not interfaces.&lt;/p&gt;
&lt;p&gt;Runtime.&lt;/p&gt;
&lt;p&gt;Zod example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const LoginSchema = z.object({
  username: z.string(),
  password: z.string(),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only parsed data enters business logic.&lt;/p&gt;
&lt;p&gt;Everything else dies at the boundary.&lt;/p&gt;
&lt;h2&gt;Runtime Validation Is Not Optional Infrastructure&lt;/h2&gt;
&lt;p&gt;This deserves its own section.&lt;/p&gt;
&lt;p&gt;Many TypeScript codebases validate inside handlers.&lt;/p&gt;
&lt;p&gt;After logic begins.&lt;/p&gt;
&lt;p&gt;After database calls.&lt;/p&gt;
&lt;p&gt;After assumptions already spread through the request pipeline.&lt;/p&gt;
&lt;p&gt;Validation belongs at the trust boundary.&lt;/p&gt;
&lt;p&gt;Modern stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zod&lt;/li&gt;
&lt;li&gt;class-validator&lt;/li&gt;
&lt;li&gt;Valibot&lt;/li&gt;
&lt;li&gt;io-ts&lt;/li&gt;
&lt;li&gt;custom parsers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specific library matters less than the architectural rule:&lt;/p&gt;
&lt;p&gt;external data is untrusted until runtime validation succeeds.&lt;/p&gt;
&lt;p&gt;This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API requests&lt;/li&gt;
&lt;li&gt;webhook payloads&lt;/li&gt;
&lt;li&gt;Kafka events&lt;/li&gt;
&lt;li&gt;Redis messages&lt;/li&gt;
&lt;li&gt;JSON imports&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;browser localStorage&lt;/li&gt;
&lt;li&gt;server actions&lt;/li&gt;
&lt;li&gt;SSR hydration data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TypeScript cannot validate any of those at runtime.&lt;/p&gt;
&lt;p&gt;That is not a limitation.&lt;/p&gt;
&lt;p&gt;That is literally outside its job description.&lt;/p…</content:encoded></item><item><title>Howto Catch Risky JavaScript Bugs with eslint-plugin-security</title><link>https://jsdev.space/howto/eslint-plugin-security/</link><guid isPermaLink="true">https://jsdev.space/howto/eslint-plugin-security/</guid><description>Learn how to use eslint-plugin-security to detect dangerous JavaScript patterns, unsafe Node.js code, dynamic execution, and hidden security mistakes.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most developers already use ESLint.&lt;/p&gt;
&lt;p&gt;You probably have rules for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unused variables&lt;/li&gt;
&lt;li&gt;import sorting&lt;/li&gt;
&lt;li&gt;React hooks&lt;/li&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;TypeScript correctness&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But many projects still miss an entirely different category of bugs:&lt;/p&gt;
&lt;p&gt;security mistakes hiding inside valid JavaScript.&lt;/p&gt;
&lt;p&gt;The code compiles.&lt;/p&gt;
&lt;p&gt;Tests pass.&lt;/p&gt;
&lt;p&gt;TypeScript is happy.&lt;/p&gt;
&lt;p&gt;Production deploys successfully.&lt;/p&gt;
&lt;p&gt;Then somebody discovers an unsafe regex freezing your API server, a dynamic &lt;code&gt;require()&lt;/code&gt; loading unexpected modules, or a command injection vulnerability hiding inside a small utility function nobody questioned during review.&lt;/p&gt;
&lt;p&gt;That is exactly where &lt;a href=&quot;https://github.com/eslint-community/eslint-plugin-security&quot;&gt;eslint-plugin-security&lt;/a&gt; becomes useful.&lt;/p&gt;
&lt;p&gt;It does not replace penetration testing.&lt;/p&gt;
&lt;p&gt;It does not magically secure your application.&lt;/p&gt;
&lt;p&gt;But it can catch dangerous patterns early — directly inside your editor and CI pipeline.&lt;/p&gt;
&lt;p&gt;In this article we&apos;ll look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what eslint-plugin-security actually does&lt;/li&gt;
&lt;li&gt;how to install it&lt;/li&gt;
&lt;li&gt;how to configure ESLint v9 Flat Config&lt;/li&gt;
&lt;li&gt;real security problems it can detect&lt;/li&gt;
&lt;li&gt;common false positives&lt;/li&gt;
&lt;li&gt;how to use it in production projects&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why Normal ESLint Rules Are Not Enough&lt;/h2&gt;
&lt;p&gt;Standard ESLint focuses on code quality.&lt;/p&gt;
&lt;p&gt;It helps prevent problems like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const username = &quot;Alex&quot;;

console.log(userName);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = await fetchProfile()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;without handling the Promise properly.&lt;/p&gt;
&lt;p&gt;Useful?&lt;/p&gt;
&lt;p&gt;Absolutely.&lt;/p&gt;
&lt;p&gt;Security-focused?&lt;/p&gt;
&lt;p&gt;Not really.&lt;/p&gt;
&lt;p&gt;Security bugs usually look perfectly legal to JavaScript.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const query = req.query.script;

eval(query);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ESLint sees valid syntax.&lt;/p&gt;
&lt;p&gt;JavaScript sees valid syntax.&lt;/p&gt;
&lt;p&gt;Your attacker sees opportunity.&lt;/p&gt;
&lt;p&gt;Security linting exists because many dangerous patterns are still syntactically correct code.&lt;/p&gt;
&lt;h2&gt;Installing eslint-plugin-security&lt;/h2&gt;
&lt;p&gt;Installation is straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -D eslint-plugin-security
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;ESLint v9 Flat Config Setup&lt;/h2&gt;
&lt;p&gt;Modern ESLint projects typically use Flat Config.&lt;/p&gt;
&lt;p&gt;Create or update:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eslint.config.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Configuration example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import security from &quot;eslint-plugin-security&quot;;

export default [
  {
    plugins: {
      security,
    },

    rules: {
      ...security.configs.recommended.rules,
    },
  },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is enough to enable the recommended ruleset.&lt;/p&gt;
&lt;p&gt;You can now run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx eslint . --ext .js,.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Legacy .eslintrc Configuration&lt;/h2&gt;
&lt;p&gt;Older projects may still use .eslintrc.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;plugins&quot;: [&quot;security&quot;],
  &quot;extends&quot;: [&quot;plugin:security/recommended&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple.&lt;/p&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #1 — Dynamic &lt;code&gt;eval()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let&apos;s start with the obvious one.&lt;/p&gt;
&lt;p&gt;JavaScript&apos;s infamous &lt;code&gt;eval()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Bad example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const sourceCode = req.body.expression;

const output = eval(sourceCode);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If external input reaches this function, your application can execute arbitrary JavaScript.&lt;/p&gt;
&lt;p&gt;That is rarely what you want.&lt;/p&gt;
&lt;p&gt;The plugin flags patterns like this immediately.&lt;/p&gt;
&lt;p&gt;Safer alternatives often exist.&lt;/p&gt;
&lt;p&gt;Instead of dynamic execution:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eval(&quot;5 + 10&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;prefer explicit logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const operations = {
  add: (x: number, y: number) =&amp;gt; x + y,
  subtract: (x: number, y: number) =&amp;gt; x - y,
};

const result = operations.add(5, 10);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Less magical.&lt;/p&gt;
&lt;p&gt;Much safer.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #2 — Non-Literal require()&lt;/h2&gt;
&lt;p&gt;This rule surprises many developers.&lt;/p&gt;
&lt;p&gt;Consider:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const packageName = req.query.library;

const dependency = require(packageName);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks flexible.&lt;/p&gt;
&lt;p&gt;Also potentially dangerous.&lt;/p&gt;
&lt;p&gt;Because attackers might control what module gets loaded.&lt;/p&gt;
&lt;p&gt;Security plugins dislike dynamic imports for good reason.&lt;/p&gt;
&lt;p&gt;Prefer explicit allowlists.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allowedLibraries = {
  csv: require(&quot;./parsers/csv&quot;),
  json: require(&quot;./parsers/json&quot;),
};

const parser = allowedLibraries[userFormat];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the input selects from known modules instead of arbitrary paths.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #3 — Unsafe Child Process Execution&lt;/h2&gt;
&lt;p&gt;Node.js applications often interact with shell commands.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { exec } from &quot;node:child_process&quot;;

exec(`ping ${hostname}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks innocent.&lt;/p&gt;
&lt;p&gt;Until somebody submits:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;localhost &amp;amp;&amp;amp; rm -rf /&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Welcome to command injection.&lt;/p&gt;
&lt;p&gt;This is one of the oldest backend vulnerabilities.&lt;/p&gt;
&lt;p&gt;Safer approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { execFile } from &quot;node:child_process&quot;;

execFile(
  &quot;ping&quot;,
  [hostname],
  (error, stdout) =&amp;gt; {
    console.log(stdout);
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Arguments stay separated from the command itself.&lt;/p&gt;
&lt;p&gt;Much safer.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #4 — Object Injection&lt;/h2&gt;
&lt;p&gt;One of the more controversial rules.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const fieldName = req.query.sort;

database[fieldName] = value;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why does the plugin care?&lt;/p&gt;
&lt;p&gt;Because user-controlled property access can sometimes lead to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prototype pollution&lt;/li&gt;
&lt;li&gt;unsafe mutation&lt;/li&gt;
&lt;li&gt;unexpected behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;payload[&quot;__proto__&quot;] = {
  compromised: true,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on the environment and codebase, this can become dangerous.&lt;/p&gt;
&lt;p&gt;Safer approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allowedFields = [
  &quot;email&quot;,
  &quot;username&quot;,
  &quot;createdAt&quot;,
];

if (allowedFields.includes(fieldName)) {
  database[fieldName] = value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explicit validation dramatically reduces risk.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #5 — Unsafe Regular Expressions&lt;/h2&gt;
&lt;p&gt;Regex performance issues are underrated.&lt;/p&gt;
&lt;p&gt;A poorly designed regex can lock an application thread.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const pattern =
  /(a+)+$/;

pattern.test(userInput);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certain inputs can trigger catastrophic backtracking.&lt;/p&gt;
&lt;p&gt;Your server CPU spikes.&lt;/p&gt;
&lt;p&gt;Requests hang.&lt;/p&gt;
&lt;p&gt;Users complain.&lt;/p&gt;
&lt;p&gt;This category is often called ReDoS — Regular Expression Denial of Service.&lt;/p&gt;
&lt;p&gt;The security plugin warns about suspicious constructions.&lt;/p&gt;
&lt;p&gt;Even better: test regex performance intentionally.&lt;/p&gt;
&lt;p&gt;Example safer pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const emailMatcher =
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple patterns usually behave more predictably.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #6 — Timing Attack Risks&lt;/h2&gt;
&lt;p&gt;Some rules target insecure comparisons.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (
  suppliedSecret === process.env.API_SECRET
) {
  authenticate();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks harmless.&lt;/p&gt;
&lt;p&gt;But direct string comparison may leak timing information.&lt;/p&gt;
&lt;p&gt;In sensitive environments, use dedicated cryptographic helpers.&lt;/p&gt;
&lt;p&gt;Node.js provides:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import crypto from &quot;node:crypto&quot;;

const verified =
  crypto.timingSafeEqual(
    Buffer.from(inputSecret),
    Buffer.from(secretValue)
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do most applications need this?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;Do authentication systems care?&lt;/p&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;h2&gt;False Positives: When the Plugin Gets Noisy&lt;/h2&gt;
&lt;p&gt;This is important.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eslint-plugin-security&lt;/code&gt; is not perfect.&lt;/p&gt;
&lt;p&gt;Some rules intentionally behave aggressively.&lt;/p&gt;
&lt;p&gt;You may see warnings like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config[key] = value;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;even when your code is completely safe.&lt;/p&gt;
&lt;p&gt;That does not automatically mean the plugin is wrong.&lt;/p&gt;
&lt;p&gt;Security tooling often prefers better safe than sorry.&lt;/p&gt;
&lt;p&gt;Still, developers need practical workflows.&lt;/p&gt;
&lt;p&gt;You have several opt…</content:encoded></item><item><title>5 Docker Compose Settings Missing From Most Production Setups</title><link>https://jsdev.space/docker-compose-production-settings/</link><guid isPermaLink="true">https://jsdev.space/docker-compose-production-settings/</guid><description>Your docker-compose.yml may work locally, but production exposes missing limits, restart policies, log rotation, healthchecks, and backup gaps.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You wrote a &lt;code&gt;docker-compose.yml&lt;/code&gt;, started everything locally, and it worked perfectly.&lt;/p&gt;
&lt;p&gt;You deployed it to a server.&lt;/p&gt;
&lt;p&gt;Then you ran:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything looked fine for a week.&lt;/p&gt;
&lt;p&gt;Then Postgres quietly consumed all available memory, Linux triggered the OOM killer, and your application died instead of the database.&lt;/p&gt;
&lt;p&gt;Or your service crashed at 2 AM and stayed offline because Docker&apos;s default restart policy is effectively:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;restart: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or container logs silently grew to 40 GB and filled the disk.&lt;/p&gt;
&lt;p&gt;None of these problems are exotic. They are common production failures caused by Compose files that were written for local development and then promoted to servers without production guardrails.&lt;/p&gt;
&lt;p&gt;The uncomfortable part is that most of these failures are fixed with a few lines of configuration.&lt;/p&gt;
&lt;p&gt;The problem is that local development rarely forces you to care. Your laptop has enough RAM, plenty of disk space, and you are usually sitting next to the terminal when something breaks. A production server is different. It runs unattended. It accumulates logs. It handles traffic spikes. It restarts after failures. It stores data that should not disappear because someone used the wrong flag.&lt;/p&gt;
&lt;p&gt;This article covers five Docker Compose settings that are easy to forget but painful to miss.&lt;/p&gt;
&lt;h2&gt;1. Set Memory and CPU Limits&lt;/h2&gt;
&lt;p&gt;By default, a container can use as much CPU and memory as the host allows.&lt;/p&gt;
&lt;p&gt;That sounds convenient locally. In production, it is a risk.&lt;/p&gt;
&lt;p&gt;Imagine a small server running three services:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your application&lt;/li&gt;
&lt;li&gt;Postgres&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without limits, any of them can consume enough resources to destabilize the whole machine. In many real systems, the database is the first service to grow aggressively. Postgres sees available memory and uses it. Redis may do the same if its dataset grows. Your application may spike during traffic bursts, image processing, queue jobs, or bad queries.&lt;/p&gt;
&lt;p&gt;When the host runs out of memory, Linux does not politely ask Docker which container should stop. The OOM killer chooses a process. Sometimes it kills the database. Sometimes it kills your application while it is handling hundreds of requests.&lt;/p&gt;
&lt;p&gt;Add explicit resource boundaries:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: &quot;1.0&quot;
        reservations:
          memory: 256M

  postgres:
    image: postgres:16-alpine
    deploy:
      resources:
        limits:
          memory: 1G
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;limits&lt;/code&gt; are the hard ceiling. If the container exceeds the memory limit, Docker isolates the failure to that container instead of letting it consume the entire host.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;reservations&lt;/code&gt; describe the amount of resources the service expects to have available. They are useful when Compose is used with orchestration features, but the most important part for a simple server is still the hard memory limit.&lt;/p&gt;
&lt;p&gt;You can check whether a container was killed because of memory pressure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker inspect myapp --format=&apos;{{.State.OOMKilled}}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the result is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then the container was killed after exceeding its memory allowance.&lt;/p&gt;
&lt;p&gt;For Postgres, memory limits should also influence database configuration. If you limit the container to &lt;code&gt;1G&lt;/code&gt;, setting &lt;code&gt;shared_buffers&lt;/code&gt; somewhere around &lt;code&gt;256MB&lt;/code&gt; is a reasonable starting point. The exact value depends on workload, but the important idea is simple: database tuning should match the container&apos;s actual memory budget, not the host&apos;s total memory.&lt;/p&gt;
&lt;h2&gt;2. Add a Restart Policy&lt;/h2&gt;
&lt;p&gt;Docker does not automatically restart failed containers unless you tell it to.&lt;/p&gt;
&lt;p&gt;That surprises many people.&lt;/p&gt;
&lt;p&gt;The default behavior is essentially:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;restart: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your application crashes, it stays down.&lt;/p&gt;
&lt;p&gt;That may be fine during development. It is not fine at 2 AM.&lt;/p&gt;
&lt;p&gt;For long-running services, use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;unless-stopped&lt;/code&gt; is usually the safest default for application services.&lt;/p&gt;
&lt;p&gt;It means Docker will restart the container after crashes, daemon restarts, and machine reboots. But if you intentionally stop the service, Docker respects that and does not immediately bring it back.&lt;/p&gt;
&lt;p&gt;That makes it more practical than &lt;code&gt;always&lt;/code&gt; for many small production setups. &lt;code&gt;always&lt;/code&gt; can be annoying during maintenance because Docker may restart containers you intentionally stopped.&lt;/p&gt;
&lt;p&gt;One-off jobs are different.&lt;/p&gt;
&lt;p&gt;Migrations, seed scripts, and maintenance tasks should not restart forever:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  migrator:
    image: myapp:latest
    command: [&quot;python&quot;, &quot;manage.py&quot;, &quot;migrate&quot;]
    restart: &quot;no&quot;

  app:
    image: myapp:latest
    restart: unless-stopped
    depends_on:
      migrator:
        condition: service_completed_successfully
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern avoids a common production mistake: starting the application before the schema is ready.&lt;/p&gt;
&lt;p&gt;If the migration fails, the application should not boot and pretend everything is fine. It should fail early, loudly, and predictably.&lt;/p&gt;
&lt;h2&gt;3. Rotate Container Logs&lt;/h2&gt;
&lt;p&gt;Docker&apos;s default &lt;code&gt;json-file&lt;/code&gt; logging driver writes logs to disk.&lt;/p&gt;
&lt;p&gt;If you do not configure rotation, those files can grow without a practical limit.&lt;/p&gt;
&lt;p&gt;A service logging every request at 100 requests per second can generate a surprising amount of data. Over weeks or months, logs can quietly consume tens of gigabytes.&lt;/p&gt;
&lt;p&gt;When the disk fills up, the failure is ugly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;containers may stop&lt;/li&gt;
&lt;li&gt;writes may fail&lt;/li&gt;
&lt;li&gt;Docker itself may become unstable&lt;/li&gt;
&lt;li&gt;recovery may require manual cleanup on the host&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add log rotation directly in Compose:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    logging:
      driver: json-file
      options:
        max-size: &quot;10m&quot;
        max-file: &quot;3&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means each log file can grow to 10 MB, and Docker keeps up to three files per container.&lt;/p&gt;
&lt;p&gt;So the container uses roughly 30 MB for logs before old files are rotated out.&lt;/p&gt;
&lt;p&gt;For many services, that is enough for quick diagnostics. If you need more history on the host, increase the limits:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logging:
  driver: json-file
  options:
    max-size: &quot;50m&quot;
    max-file: &quot;10&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gives you up to 500 MB per container.&lt;/p&gt;
&lt;p&gt;You can also define global defaults in &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;log-driver&quot;: &quot;json-file&quot;,
  &quot;log-opts&quot;: {
    &quot;max-size&quot;: &quot;10m&quot;,
    &quot;max-file&quot;: &quot;5&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After changing daemon settings, restart Docker so new containers use the configuration.&lt;/p&gt;
&lt;p&gt;To inspect current log usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;du -sh /var/lib/docker/containers/*/*-json.log | sort -h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see multi-gigabyte JSON log files, log rotation was missed.&lt;/p&gt;
&lt;p&gt;That is not a logging strategy. It is a delayed outage.&lt;/p&gt;
&lt;h2&gt;4. Add Healthchecks&lt;/h2&gt;
&lt;p&gt;Docker knows whether a process is running.&lt;/p&gt;
&lt;p&gt;It does not automatically know whether your application is useful.&lt;/p&gt;
&lt;p&gt;A container can be in a &lt;code&gt;running&lt;/code&gt; state while the actual service is broken. The process may exist, but the app may be stuck, overloaded, disconnected from the database, or unable to respond to HTTP requests.&lt;/p&gt;
&lt;p&gt;Add a healthcheck:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    healthcheck:
      test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-f&quot;, &quot;http://localhost:8080/health&quot;]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;test&lt;/code&gt; command should check something meaningful.&lt;/p&gt;…</content:encoded></item><item><title>Meet Fate: Building an Astro Blog with a Modern React Data Client</title><link>https://jsdev.space/meet-fate-react-astro/</link><guid isPermaLink="true">https://jsdev.space/meet-fate-react-astro/</guid><description>Explore Fate through a practical Astro 6 blog project with React islands, MDX, Tailwind v4, search, dark mode, tags, and modern client-side data patterns.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React solved UI a long time ago.
Data, however, is still where many applications quietly accumulate complexity.&lt;/p&gt;
&lt;p&gt;Between &lt;code&gt;fetch()&lt;/code&gt;, &lt;code&gt;useEffect()&lt;/code&gt;, loading states, caching layers, duplicated requests, and client synchronization, modern React applications often end up inventing their own mini data framework.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fate.technology/&quot;&gt;Fate&lt;/a&gt; takes a different approach.&lt;/p&gt;
&lt;p&gt;It is a modern React data client built around composable data views, normalized caching, and structured client data access.&lt;/p&gt;
&lt;p&gt;Although Fate can power much larger systems — dashboards, realtime applications, complex authenticated interfaces — this article explores it in a smaller and perhaps less expected environment:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;an Astro 6 static blog&lt;/strong&gt; with React islands.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; is designed around static generation. Blogs are primarily content-driven. Many projects can be built using Astro alone, without React, without a client data layer, and often without any client-side JavaScript at all.&lt;/p&gt;
&lt;p&gt;That is true.&lt;/p&gt;
&lt;p&gt;But modern Astro applications frequently mix static rendering with selective interactivity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React islands&lt;/li&gt;
&lt;li&gt;dynamic search widgets&lt;/li&gt;
&lt;li&gt;authenticated UI sections&lt;/li&gt;
&lt;li&gt;personalized dashboards&lt;/li&gt;
&lt;li&gt;API-driven components&lt;/li&gt;
&lt;li&gt;client-side stateful interfaces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once React islands enter the picture, questions about data access eventually follow.&lt;/p&gt;
&lt;p&gt;Instead of building an abstract enterprise dashboard, we will explore Fate through something smaller, concrete, and easier to reason about.&lt;/p&gt;
&lt;p&gt;By the end of this article, we will build a modern Astro 6 blog featuring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MDX content collections&lt;/li&gt;
&lt;li&gt;dynamic blog routes&lt;/li&gt;
&lt;li&gt;React islands&lt;/li&gt;
&lt;li&gt;Tailwind v4 styling&lt;/li&gt;
&lt;li&gt;client-side search&lt;/li&gt;
&lt;li&gt;dark mode&lt;/li&gt;
&lt;li&gt;tag pages&lt;/li&gt;
&lt;li&gt;API routes&lt;/li&gt;
&lt;li&gt;a minimal working Fate integration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Along the way, we will also answer a more practical question:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do you actually need Fate for an Astro blog?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Usually, no.&lt;/p&gt;
&lt;p&gt;But understanding where it fits can tell us a lot about modern React data architecture.&lt;/p&gt;
&lt;h2&gt;What Is Fate?&lt;/h2&gt;
&lt;p&gt;Before touching Astro, it is worth understanding what Fate actually is.&lt;/p&gt;
&lt;p&gt;Fate is not a state manager.&lt;/p&gt;
&lt;p&gt;It is not another &lt;code&gt;useState()&lt;/code&gt; replacement.&lt;/p&gt;
&lt;p&gt;It is not React Query with slightly different naming.&lt;/p&gt;
&lt;p&gt;Instead, Fate positions itself as a modern React data client.&lt;/p&gt;
&lt;p&gt;Its API revolves around concepts like views, requests, transports, and structured client data access.&lt;/p&gt;
&lt;p&gt;One of the most recognizable pieces of the API is &lt;code&gt;view()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { view } from &quot;react-fate&quot;;

type Post = {
  id: string;
  title: string;
  description: string;
};

const PostView = view&amp;lt;Post&amp;gt;()({
  id: true,
  title: true,
  description: true,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of thinking primarily in terms of raw network requests, Fate encourages components to declare which pieces of data they actually care about.&lt;/p&gt;
&lt;p&gt;If this feels somewhat familiar to GraphQL fragments, Relay, or normalized client architectures, that is not accidental.&lt;/p&gt;
&lt;p&gt;The idea is not simply &lt;em&gt;&quot;fetch some JSON&quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The idea is structured client-side data composition.&lt;/p&gt;
&lt;p&gt;That distinction becomes much more interesting once applications begin growing beyond a few isolated components.&lt;/p&gt;
&lt;h2&gt;Why Use Fate in an Astro Project?&lt;/h2&gt;
&lt;p&gt;At this point, a reasonable question appears.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Astro? And why Fate inside Astro?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After all, Astro was built around a static-first philosophy.&lt;/p&gt;
&lt;p&gt;A typical Astro blog can happily exist without React, without client-side state, and without a dedicated data layer.&lt;/p&gt;
&lt;p&gt;In many cases, that is exactly what makes Astro attractive.&lt;/p&gt;
&lt;p&gt;You write content.&lt;/p&gt;
&lt;p&gt;Astro generates HTML.&lt;/p&gt;
&lt;p&gt;The browser receives mostly static pages.&lt;/p&gt;
&lt;p&gt;Everything stays fast.&lt;/p&gt;
&lt;p&gt;A minimal blog can often be implemented using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Markdown or MDX content&lt;/li&gt;
&lt;li&gt;static routes&lt;/li&gt;
&lt;li&gt;server-side content collections&lt;/li&gt;
&lt;li&gt;zero client JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a large percentage of publishing websites, this is already enough.&lt;/p&gt;
&lt;p&gt;So introducing React — and especially a specialized React data client — might initially sound like unnecessary complexity.&lt;/p&gt;
&lt;p&gt;That concern is fair.&lt;/p&gt;
&lt;p&gt;However, modern Astro projects frequently extend beyond purely static pages.&lt;/p&gt;
&lt;p&gt;Consider a few examples.&lt;/p&gt;
&lt;p&gt;An otherwise static blog might eventually gain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client-side search&lt;/li&gt;
&lt;li&gt;bookmarks&lt;/li&gt;
&lt;li&gt;reading history&lt;/li&gt;
&lt;li&gt;personalized recommendations&lt;/li&gt;
&lt;li&gt;authenticated dashboards&lt;/li&gt;
&lt;li&gt;realtime counters&lt;/li&gt;
&lt;li&gt;notification panels&lt;/li&gt;
&lt;li&gt;interactive filtering systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where &lt;strong&gt;Astro islands architecture&lt;/strong&gt; becomes interesting.&lt;/p&gt;
&lt;p&gt;Astro allows us to selectively introduce interactivity only where it is needed.&lt;/p&gt;
&lt;p&gt;Instead of hydrating an entire application, we can hydrate isolated components.&lt;/p&gt;
&lt;p&gt;A search widget can become interactive.&lt;/p&gt;
&lt;p&gt;A theme toggle can manage client state.&lt;/p&gt;
&lt;p&gt;A live dashboard card can fetch dynamic data.&lt;/p&gt;
&lt;p&gt;Everything else remains static.&lt;/p&gt;
&lt;p&gt;That balance is one of Astro&apos;s strongest design choices.&lt;/p&gt;
&lt;p&gt;For example, a React search component can be hydrated independently:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;BlogSearch
  posts={searchPosts}
  client:load
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only this island becomes interactive.&lt;/p&gt;
&lt;p&gt;The rest of the page remains static HTML.&lt;/p&gt;
&lt;p&gt;This approach gives us a useful playground for exploring Fate.&lt;/p&gt;
&lt;p&gt;We do not need a massive enterprise application to understand a data client.&lt;/p&gt;
&lt;p&gt;We simply need a project where static content and client-side interactivity coexist.&lt;/p&gt;
&lt;p&gt;An Astro blog happens to be a surprisingly practical environment for exactly that.&lt;/p&gt;
&lt;h2&gt;Creating the Project&lt;/h2&gt;
&lt;p&gt;We will begin with a minimal Astro 6 setup.&lt;/p&gt;
&lt;p&gt;Create a new project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create astro@latest fate-blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Choose the following options during initialization:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Template: Empty
TypeScript: Yes
Install dependencies: Yes
Initialize Git: Yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into the project directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd fate-blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the pieces we will use throughout the article.&lt;/p&gt;
&lt;p&gt;React integration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx astro add react
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MDX support:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx astro add mdx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tailwind v4:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx astro add tailwind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, install Fate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install react-fate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our dependency list will look roughly like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;dependencies&quot;: {
  &quot;@astrojs/mdx&quot;: &quot;^5.0.6&quot;,
  &quot;@astrojs/react&quot;: &quot;^5.0.5&quot;,
  &quot;@tailwindcss/vite&quot;: &quot;^4.3.0&quot;,
  &quot;@types/react&quot;: &quot;^19.2.15&quot;,
  &quot;@types/react-dom&quot;: &quot;^19.2.3&quot;,
  &quot;astro&quot;: &quot;^6.3.6&quot;,
  &quot;react&quot;: &quot;^19.2.6&quot;,
  &quot;react-dom&quot;: &quot;^19.2.6&quot;,
  &quot;react-fate&quot;: &quot;^1.0.3&quot;,
  &quot;tailwindcss&quot;: &quot;^4.3.0&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuring Content Collections in Astro 6&lt;/h3&gt;
&lt;p&gt;Astro 6 uses the newer content loader approach.&lt;/p&gt;
&lt;p&gt;Create:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/content.config.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { defineCollection, z } from &quot;astro:content&quot;;
import { glob } from &quot;astro/loaders&quot;;

const posts = defineCollection({
  loader: glob({
    pattern: &quot;**/*.{md,mdx}&quot;,
    base: &quot;./src/content/posts&quot;,
  }),

  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.coerce.date(),
    category: z.string(),
    tags: z.array(z.string()).default([]),
  }),
});

export const collections = {
  posts,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration defines a typed content collection named &lt;code&gt;posts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every post must now provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;description&lt;/li&gt;
&lt;li&gt;publication date&lt;/li&gt;
&lt;li&gt;cat…</content:encoded></item><item><title>Friday Links #38 — JavaScript Trends, AI Dev Tools &amp; Releases</title><link>https://jsdev.space/friday/friday-38/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-38/</guid><description>A curated roundup of JavaScript news, AI developer tools, framework releases, browser updates, and standout projects from the modern web ecosystem.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-38.png&quot; alt=&quot;Friday Links #38&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another busy week in the JavaScript world.&lt;/p&gt;
&lt;p&gt;Framework releases kept shipping, browser vendors continued experimenting, AI development tools evolved at an uncomfortable speed, and open-source maintainers somehow still found time to launch new libraries, runtimes, and developer utilities.&lt;/p&gt;
&lt;p&gt;This edition collects the updates, experiments, technical discussions, and releases worth paying attention to — from frontend frameworks and tooling changes to AI coding workflows, browser platform news, and interesting projects from across the developer ecosystem.&lt;/p&gt;
&lt;p&gt;Whether you are following React, TypeScript, Bun, Node.js, AI-assisted development, or simply looking for useful tools to explore, here are the links that stood out this week.&lt;/p&gt;
&lt;h2&gt;📢 Special partner message&lt;/h2&gt;
&lt;h3&gt;🗓️ &lt;a href=&quot;https://reactnorway.com&quot;&gt;React Norway&lt;/a&gt; — Oslo, June 5 🇳🇴&lt;/h3&gt;
&lt;p&gt;Join 350+ React and full-stack developers for a unique single-track conference with a strong “Rock &amp;amp; React” vibe, great talks, community energy, and modern web engineering discussions.&lt;/p&gt;
&lt;h3&gt;🎟️ Use code &lt;code&gt;TWIR&lt;/code&gt; for 10% off your ticket.&lt;/h3&gt;
&lt;h2&gt;🧠 Language &amp;amp; Runtime Updates&lt;/h2&gt;
&lt;h3&gt;TypeScript 6.0 Is Official — And It’s Preparing the Road to TypeScript 7&lt;/h3&gt;
&lt;p&gt;One of the biggest ecosystem updates remains &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html&quot;&gt;TypeScript 6.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This release is not just another incremental version bump. It acts as a transition layer between the current JavaScript-based compiler and the upcoming native TypeScript 7 compiler. According to the TypeScript team, 6.0 is effectively the bridge release that prepares projects for the future architecture.&lt;/p&gt;
&lt;p&gt;Some notable changes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;alignment with future TS 7 behavior&lt;/li&gt;
&lt;li&gt;deprecations aimed at modern ESM workflows&lt;/li&gt;
&lt;li&gt;simplified DOM library behavior&lt;/li&gt;
&lt;li&gt;stricter compiler rules&lt;/li&gt;
&lt;li&gt;migration tooling for upcoming changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The broader direction is clear: TypeScript is increasingly optimizing for modern evergreen runtimes, ESM tooling, and native performance improvements.&lt;/p&gt;
&lt;h3&gt;Bun’s Rust Rewrite Has Been Merged&lt;/h3&gt;
&lt;p&gt;The Bun story continues.&lt;/p&gt;
&lt;p&gt;One of the more talked-about updates recently: &lt;a href=&quot;https://github.com/oven-sh/bun/pull/30412&quot;&gt;Bun’s experimental Rust rewrite effort has now been merged&lt;/a&gt;, generating substantial discussion across developer communities. Questions quickly emerged around maintainability, AI-assisted code generation quality, and long-term runtime strategy.&lt;/p&gt;
&lt;p&gt;At the same time, Bun keeps strengthening its position inside the TypeScript ecosystem.&lt;/p&gt;
&lt;p&gt;Developers increasingly evaluate Bun not just as a runtime, but as a broader replacement for parts of the traditional Node toolchain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;package manager&lt;/li&gt;
&lt;li&gt;bundler&lt;/li&gt;
&lt;li&gt;test runner&lt;/li&gt;
&lt;li&gt;TypeScript execution environment&lt;/li&gt;
&lt;li&gt;server runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The “Should new TypeScript projects start with Bun?” conversation is becoming more common.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/deno.land/post/3mm6clkq5uc22&quot;&gt;Deno 2.8 ships this week&lt;/a&gt; with a focus on improving the developer experience around TypeScript, ESM, and modern JavaScript features.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/install-element-ot&quot;&gt;Install web apps with the new HTML install element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/prompts-advisory-structure-binding-daniel-meyer-cpxce/&quot;&gt;Prompts are advisory. Structure is binding.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://grahamhelton.com/blog/ssh-cheatsheet&quot;&gt;An Excruciatingly Detailed Guide To SSH&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://typesetinthefuture.com/2016/02/18/futuristic/&quot;&gt;How To Make Your Text Look Futuristic&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.thenodebook.com/&quot;&gt;MASTER THE NODE.JS INTERNALS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/using-nonexistent-nth-letter-selector-now/&quot;&gt;Let’s Use the Nonexistent ::nth-letter Selector Now&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tanstack.com/blog/incident-followup&quot;&gt;Hardening TanStack After the npm Compromise&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://polypane.app/blog/using-safe-area-inset-to-build-mobile-safe-layouts/&quot;&gt;Using safe-area-inset to build mobile-safe layouts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejsdesignpatterns.com/blog/whats-new-in-nodejs-26/&quot;&gt;What&apos;s new in Node.js 26&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.sentry.io/fixing-javascript-observability/&quot;&gt;Fixing JavaScript observability, one library at a time&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.gaborkoos.com/posts/2026-05-09-Your-Recursion-Is-Lying-to-You/&quot;&gt;Your Recursion Is Lying to You&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jasnell.me/posts/quic-part-4&quot;&gt;HTTP/3 Over QUIC in Node.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rubrik.com/blog/architecture/26/2/async-react-building-non-blocking-uis-with-usetransition-and-useactionstate&quot;&gt;Async React: Building Non-Blocking UIs with useTransition and useActionState&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ishadeed.com/article/css-round/&quot;&gt;Better fluid sizing with round()&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jvns.ca/blog/2026/05/15/moving-away-from-tailwind--and-learning-to-structure-my-css-/&quot;&gt;Moving away from Tailwind, and learning to structure my CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://evilmartians.com/chronicles/600-million-people-write-right-to-left-2-fixes-your-app-needs&quot;&gt;600+ million people write right-to-left: 2 fixes your app needs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/gap-decorations-stable?hl=en&quot;&gt;Gap decorations: Now available in Chromium&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://chrismorgan.info/css-themed-colours&quot;&gt;A few ways of specifying per-theme colours in only CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/when-to-use-and-not-use-css-shorthand-properties&quot;&gt;When to use (and not use) CSS shorthand properties&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tympanus.net/codrops/2025/02/04/how-to-make-the-fluffiest-grass-with-three-js/&quot;&gt;How to Make The Fluffiest Grass With Three.js&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;Open-source Discord alternative &lt;a href=&quot;https://github.com/Milkshiift/GoofCord&quot;&gt;GoofCord&lt;/a&gt; has been released, promising a faster, cleaner, and far more customizable experience than the official client.&lt;/p&gt;
&lt;p&gt;According to the project, GoofCord:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;runs noticeably faster than the standard Discord client, with fewer slowdowns and UI hiccups;&lt;/li&gt;
&lt;li&gt;blocks built-in telemetry and user data collection;&lt;/li&gt;
&lt;li&gt;supports password-encrypted conversations;&lt;/li&gt;
&lt;li&gt;allows screen sharing at any resolution and frame rate;&lt;/li&gt;
&lt;li&gt;lets users choose which application audio gets streamed;&lt;/li&gt;
&lt;li&gt;automatically updates your status based on games, music, or videos;&lt;/li&gt;
&lt;li&gt;supports Vencord, Equicord, and Shelter customization plugins out of the box;&lt;/li&gt;
&lt;li&gt;includes global hotkeys that keep working even when the app is minimized;&lt;/li&gt;
&lt;li&gt;supports audio streaming on Linux, while also running on Windows and macOS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/alternbits/awesome-cuda-books&quot;&gt;Awesome CUDA Books&lt;/a&gt; is a new curated list of resources for learning CUDA programming, covering everything from beginner-friendly introductions to advanced optimization techniques.&lt;/p&gt;
&lt;p&gt;An open-source project called &lt;a href=&quot;https://github.com/MikeVeerman/tokenspeed&quot;&gt;tokenspeed&lt;/a&gt; (including an &lt;a href=&quot;https://mikeveerman.github.io/tokenspeed/?rate=30&amp;amp;mode=code&quot;&gt;online version&lt;/a&gt;) has been released to make LLM token throughput easier to understand visually.&lt;/p&gt;
&lt;p&gt;Most local LLM benchmarks report raw generation speed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;47 tokens/sec on an M3&lt;/li&gt;
&lt;li&gt;180 tokens/sec on an RTX 4090&lt;/li&gt;
&lt;li&gt;500 tokens/sec on Groq&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But unless you&apos;ve actually watched tokens stream at those speeds, those numbers can feel pretty abstract.&lt;/p&gt;
&lt;p&gt;tokenspeed solves that problem.&lt;/p&gt;
&lt;p&gt;It’s a terminal utility that simulates token streaming at any speed you choose, allowing you to see what different throughput numbers actu…</content:encoded></item><item><title>How JavaScript call, apply, and bind Actually Work</title><link>https://jsdev.space/howto/call-apply-bind/</link><guid isPermaLink="true">https://jsdev.space/howto/call-apply-bind/</guid><description>Deep dive into JavaScript call, apply, bind, and arguments. Learn explicit binding, handwritten implementations, and real language internals.</description><pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaScript gives functions a strange amount of power.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/call-apply-bind-deep-dive.png&quot; alt=&quot;call, apply, bind, and arguments&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A function can be called directly. It can be stored in a variable. It can be passed into another function. It can become a method of an object. It can be used as a constructor. It can even be executed with a completely different &lt;code&gt;this&lt;/code&gt; value from the one you expected.&lt;/p&gt;
&lt;p&gt;That flexibility is one of the reasons JavaScript is so expressive. It is also one of the reasons &lt;code&gt;this&lt;/code&gt; has confused developers for decades.&lt;/p&gt;
&lt;p&gt;The methods &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, and &lt;code&gt;bind&lt;/code&gt; sit right in the middle of that confusion. Most developers know their surface-level syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;functionName.call(context, arg1, arg2)
functionName.apply(context, [arg1, arg2])
const boundFunction = functionName.bind(context, arg1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But syntax is not understanding.&lt;/p&gt;
&lt;p&gt;Real understanding starts when you can explain why these APIs exist, what invocation rules they rely on, how to implement simplified versions yourself, and where the edge cases begin.&lt;/p&gt;
&lt;p&gt;This article takes a deep look at &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, &lt;code&gt;bind&lt;/code&gt;, and &lt;code&gt;arguments&lt;/code&gt; from the perspective of JavaScript language mechanics. The goal is not to memorize another interview trick. The goal is to understand what actually happens when a function is invoked.&lt;/p&gt;
&lt;h2&gt;Why These APIs Exist&lt;/h2&gt;
&lt;p&gt;The main problem solved by &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, and &lt;code&gt;bind&lt;/code&gt; is context control.&lt;/p&gt;
&lt;p&gt;In JavaScript, &lt;code&gt;this&lt;/code&gt; is not determined only by where a function is written. For regular functions, &lt;code&gt;this&lt;/code&gt; is mainly determined by how the function is called.&lt;/p&gt;
&lt;p&gt;That is the part many developers miss.&lt;/p&gt;
&lt;p&gt;Consider this function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function showCurrentUser() {
  console.log(this.username)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is no permanent &lt;code&gt;this&lt;/code&gt; value inside this function. The value depends on the invocation pattern.&lt;/p&gt;
&lt;p&gt;Call it directly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;showCurrentUser()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In non-strict browser code, &lt;code&gt;this&lt;/code&gt; may point to &lt;code&gt;window&lt;/code&gt;. In strict mode, &lt;code&gt;this&lt;/code&gt; will be &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Call it as an object method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const account = {
  username: &apos;Maya&apos;,
  showCurrentUser,
}

account.showCurrentUser()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;this&lt;/code&gt; points to &lt;code&gt;account&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Call it with &lt;code&gt;call&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;showCurrentUser.call({ username: &apos;Nina&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;this&lt;/code&gt; points to the object passed into &lt;code&gt;call&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Same function. Different result. Different invocation.&lt;/p&gt;
&lt;p&gt;That is why the best mental model is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most &lt;code&gt;this&lt;/code&gt; bugs are invocation bugs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not declaration bugs. Not class bugs. Not file structure bugs. Invocation bugs.&lt;/p&gt;
&lt;h2&gt;The Binding Rules Behind &lt;code&gt;this&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Before implementing &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, or &lt;code&gt;bind&lt;/code&gt;, we need to understand the binding rules they interact with.&lt;/p&gt;
&lt;p&gt;JavaScript has several important ways to determine &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Default Binding&lt;/h3&gt;
&lt;p&gt;Default binding happens when a regular function is called without an owning object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function printContext() {
  console.log(this)
}

printContext()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In strict mode, &lt;code&gt;this&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use strict&apos;

function printContext() {
  console.log(this)
}

printContext() // undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In older non-strict browser code, &lt;code&gt;this&lt;/code&gt; can fall back to the global object.&lt;/p&gt;
&lt;p&gt;Modern code should avoid relying on this behavior. It is fragile and often different between environments.&lt;/p&gt;
&lt;h3&gt;Implicit Binding&lt;/h3&gt;
&lt;p&gt;Implicit binding happens when a function is called as a property of an object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const dashboard = {
  title: &apos;Admin Panel&apos;,
  printTitle() {
    console.log(this.title)
  },
}

dashboard.printTitle()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The call expression is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dashboard.printTitle()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the function is called through &lt;code&gt;dashboard&lt;/code&gt;, JavaScript binds &lt;code&gt;this&lt;/code&gt; to &lt;code&gt;dashboard&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This rule is extremely important because a simplified implementation of &lt;code&gt;call&lt;/code&gt; can exploit it.&lt;/p&gt;
&lt;h3&gt;Explicit Binding&lt;/h3&gt;
&lt;p&gt;Explicit binding happens when you use &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, or &lt;code&gt;bind&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function printRole() {
  console.log(this.role)
}

printRole.call({ role: &apos;editor&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the function is not called as a method of the object. We explicitly provide the object that should become &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That is the purpose of &lt;code&gt;call&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt;: execute a function immediately with a chosen &lt;code&gt;this&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bind&lt;/code&gt; also chooses a &lt;code&gt;this&lt;/code&gt; value, but it does not execute immediately. It returns a new function that remembers the chosen context.&lt;/p&gt;
&lt;h3&gt;Constructor Binding&lt;/h3&gt;
&lt;p&gt;Constructor binding happens when a function is called with &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Product(name) {
  this.name = name
}

const book = new Product(&apos;JavaScript Guide&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;new Product(...)&lt;/code&gt; runs, JavaScript creates a new object and binds &lt;code&gt;this&lt;/code&gt; to that new object during the constructor call.&lt;/p&gt;
&lt;p&gt;This matters because constructor binding has higher priority than a simple bound context. A complete &lt;code&gt;bind&lt;/code&gt; polyfill must handle this case. Many simplified examples do not.&lt;/p&gt;
&lt;h2&gt;The Basic Difference Between call, apply, and bind&lt;/h2&gt;
&lt;p&gt;At a high level, the three APIs differ in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Whether the function executes immediately.&lt;/li&gt;
&lt;li&gt;How arguments are passed.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;fn.call(context, arg1, arg2, arg3)
fn.apply(context, [arg1, arg2, arg3])
const later = fn.bind(context, arg1, arg2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;call&lt;/code&gt; executes immediately and receives arguments one by one.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apply&lt;/code&gt; executes immediately and receives arguments as an array-like list.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bind&lt;/code&gt; does not execute immediately. It returns a new function with a fixed context and optional preset arguments.&lt;/p&gt;
&lt;p&gt;That sounds simple, but the real mechanics are more interesting.&lt;/p&gt;
&lt;h2&gt;Implementing a Simple call&lt;/h2&gt;
&lt;p&gt;Let us start from scratch.&lt;/p&gt;
&lt;p&gt;All functions inherit from &lt;code&gt;Function.prototype&lt;/code&gt;, so we can add our own method there for educational purposes.&lt;/p&gt;
&lt;p&gt;Do not patch built-in prototypes in production code. This is only for learning.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Function.prototype.runNow = function () {
  console.log(&apos;custom method executed&apos;)
}

function calculateTotal() {
  console.log(&apos;original function executed&apos;)
}

calculateTotal.runNow()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;custom method executed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it does not execute &lt;code&gt;calculateTotal&lt;/code&gt; itself.&lt;/p&gt;
&lt;p&gt;Inside &lt;code&gt;runNow&lt;/code&gt;, what does &lt;code&gt;this&lt;/code&gt; refer to?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Function.prototype.runNow = function () {
  console.log(this)
}

calculateTotal.runNow()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value of &lt;code&gt;this&lt;/code&gt; is the function that called &lt;code&gt;runNow&lt;/code&gt;. In this case, &lt;code&gt;calculateTotal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That means we can execute it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Function.prototype.runNow = function () {
  const targetFunction = this
  return targetFunction()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function sayHello() {
  return &apos;Hello&apos;
}

console.log(sayHello.runNow())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have recreated the first part of &lt;code&gt;call&lt;/code&gt;: invoking the function through a prototype method.&lt;/p&gt;
&lt;p&gt;Now we need to control &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How call Changes this&lt;/h2&gt;
&lt;p&gt;Suppose we want this behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function printUser() {
  console.log(this.n…</content:encoded></item><item><title>Howto Master the Blob for File Handling and Memory Optimization</title><link>https://jsdev.space/howto/master-blob-api/</link><guid isPermaLink="true">https://jsdev.space/howto/master-blob-api/</guid><description>Learn how to use the Blob API for file processing, uploads, previews, downloads, and memory optimization in modern JavaScript applications.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern frontend applications deal with files all the time.&lt;/p&gt;
&lt;p&gt;Users upload avatars, drop videos into dashboards, export CSV reports, preview PDFs, download generated configuration files, and work with media directly in the browser. At first, these features look simple. A file input, a preview element, maybe a download button, and the job seems done.&lt;/p&gt;
&lt;p&gt;Then the real problems appear.&lt;/p&gt;
&lt;p&gt;Large files freeze the tab. Image uploads are too slow. Data URLs become huge. Browser memory keeps growing. Previews work for one file type but not another. A dashboard that looked fine during testing becomes unstable after a user processes dozens of images in a single session.&lt;/p&gt;
&lt;p&gt;This is where the &lt;code&gt;Blob&lt;/code&gt; API becomes important.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Blob&lt;/code&gt; is not just a small browser feature for downloads. It is one of the core building blocks behind file handling in modern JavaScript. Once you understand how Blob objects work, you can build cleaner upload flows, safer previews, better export tools, and more memory-efficient interfaces.&lt;/p&gt;
&lt;p&gt;This article walks through practical Blob usage in real frontend work: creating Blob objects, processing large files in chunks, compressing images, building previews, generating downloads, and avoiding memory leaks caused by forgotten object URLs.&lt;/p&gt;
&lt;h2&gt;What Is a Blob?&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;Blob&lt;/code&gt; is an immutable object that represents raw data.&lt;/p&gt;
&lt;p&gt;That data can be text, JSON, CSV, an image, a video, a PDF, or any other binary content the browser can hold. The word “Blob” stands for Binary Large Object, but in frontend development you can think of it as a file-like container created or handled inside the browser.&lt;/p&gt;
&lt;p&gt;A minimal Blob looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const textBlob = new Blob([&apos;Hello from the Blob API&apos;], {
  type: &apos;text/plain&apos;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first argument is an array of data parts. The second argument describes the Blob. Most of the time, the most important option is &lt;code&gt;type&lt;/code&gt;, which stores the MIME type.&lt;/p&gt;
&lt;p&gt;Examples of common MIME types:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;text/plain
application/json
text/csv
image/png
image/jpeg
application/pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The MIME type helps the browser understand how to treat the data. A JSON Blob, an image Blob, and a PDF Blob may all be Blob objects, but the browser can preview, download, or send them differently depending on the type.&lt;/p&gt;
&lt;h2&gt;Why Blob Is Better Than Large Data URLs&lt;/h2&gt;
&lt;p&gt;A common beginner approach for generating files is to create a &lt;code&gt;data:&lt;/code&gt; URL manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const hugeText = &apos;Some large content...&apos;.repeat(100_000);

const url =
  &apos;data:text/plain;charset=utf-8,&apos; +
  encodeURIComponent(hugeText);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works for small examples, but it scales badly. Large data URLs can consume more memory than expected, become difficult for the browser to handle, and fail when the generated URL becomes too long.&lt;/p&gt;
&lt;p&gt;Blob is usually the better option:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const hugeText = &apos;Some large content...&apos;.repeat(100_000);

const blob = new Blob([hugeText], {
  type: &apos;text/plain&apos;,
});

const url = URL.createObjectURL(blob);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of encoding everything into a giant string URL, the browser creates an object URL that points to the Blob data internally. That is cleaner, more memory-friendly, and more reliable for larger content.&lt;/p&gt;
&lt;h2&gt;Creating Blob Objects Properly&lt;/h2&gt;
&lt;p&gt;Blob creation is simple, but in real projects it is worth wrapping it in small utilities so your code stays consistent.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type BlobOptionsInput = {
  mimeType?: string;
};

function createFileBlob(
  parts: BlobPart[],
  options: BlobOptionsInput = {}
) {
  return new Blob(parts, {
    type: options.mimeType ?? &apos;text/plain&apos;,
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can create different file-like objects with explicit MIME types.&lt;/p&gt;
&lt;h3&gt;Text Blob&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const readmeBlob = createFileBlob(
  [&apos;This file was generated in the browser.&apos;],
  { mimeType: &apos;text/plain&apos; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;JSON Blob&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const userSettings = {
  theme: &apos;dark&apos;,
  compactMode: true,
  language: &apos;en&apos;,
};

const settingsBlob = createFileBlob(
  [JSON.stringify(userSettings, null, 2)],
  { mimeType: &apos;application/json&apos; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HTML Blob&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const htmlDocument = `
  &amp;lt;!doctype html&amp;gt;
  &amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;h1&amp;gt;Generated HTML&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
`;

const htmlBlob = createFileBlob(
  [htmlDocument],
  { mimeType: &apos;text/html&apos; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main rule is simple: always set the correct MIME type when you know what kind of data you are creating.&lt;/p&gt;
&lt;h2&gt;Downloading Generated Files in the Browser&lt;/h2&gt;
&lt;p&gt;One of the most common Blob use cases is generating downloadable files without asking the server to create them.&lt;/p&gt;
&lt;p&gt;For example, a settings page may allow users to export their configuration as JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function downloadJsonFile(
  data: unknown,
  filename = &apos;settings.json&apos;
) {
  const json = JSON.stringify(data, null, 2);

  const blob = new Blob([json], {
    type: &apos;application/json&apos;,
  });

  const url = URL.createObjectURL(blob);

  const link = document.createElement(&apos;a&apos;);
  link.href = url;
  link.download = filename;
  link.style.display = &apos;none&apos;;

  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);

  URL.revokeObjectURL(url);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;downloadJsonFile(
  {
    editor: &apos;VS Code&apos;,
    theme: &apos;dark&apos;,
    autosave: true,
  },
  &apos;developer-settings.json&apos;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern is useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;exporting user settings&lt;/li&gt;
&lt;li&gt;downloading generated reports&lt;/li&gt;
&lt;li&gt;creating local backups&lt;/li&gt;
&lt;li&gt;exporting analytics data&lt;/li&gt;
&lt;li&gt;generating JSON, CSV, or text files from client-side state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The important part is cleanup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;URL.revokeObjectURL(url);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whenever you create an object URL, you should also decide when to revoke it.&lt;/p&gt;
&lt;h2&gt;Processing Large Files in Chunks&lt;/h2&gt;
&lt;p&gt;Reading a large file all at once can hurt performance.&lt;/p&gt;
&lt;p&gt;This looks harmless:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const reader = new FileReader();

reader.onload = () =&amp;gt; {
  const content = reader.result;
  console.log(content);
};

reader.readAsText(file);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For small files, this is fine. For large files, it can freeze the page or consume too much memory.&lt;/p&gt;
&lt;p&gt;The safer pattern is chunked processing.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;File&lt;/code&gt; is a kind of Blob, so it supports the &lt;code&gt;slice()&lt;/code&gt; method. This allows you to read a file piece by piece.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function processFileInChunks(
  file: File,
  chunkSize = 1024 * 1024
) {
  const totalChunks = Math.ceil(file.size / chunkSize);

  for (let chunkIndex = 0; chunkIndex &amp;lt; totalChunks; chunkIndex++) {
    const start = chunkIndex * chunkSize;
    const end = Math.min(start + chunkSize, file.size);

    const chunk = file.slice(start, end);

    await processChunk(chunk, chunkIndex, totalChunks);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A simple chunk reader can look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function readBlobAsText(blob: Blob): Promise&amp;lt;string&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    const reader = new FileReader();

    reader.onload = () =&amp;gt; {
      resolve(String(reader.result ?? &apos;&apos;));
    };

    reader.onerror = () =&amp;gt; {
      reject(reader.error);
    };

    reader.readAsText(blob);
  });
}

async function processChunk(
  chunk: Blob,
  index: number,
  total: number
) {
  const text = await readBlobAsText(chunk);

  console.log({
    index,
    total,
    size: chunk.size,
    preview: text.slice(0, 80),
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach gives you more control over memory usage and makes it easier to show progress.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function processFileWithProgress(
  file: File,
  onProgress: (percentage: number) =&amp;gt; void
) {
  const chunkSize = 1024 * 1024;
  const totalChunks = Math.ceil(file.size / chunkSize);

  for (let index =…</content:encoded></item><item><title>Howto Undo Git Commits Without Breaking Your History</title><link>https://jsdev.space/howto/undo-git-commits/</link><guid isPermaLink="true">https://jsdev.space/howto/undo-git-commits/</guid><description>Learn when to use Git reset, revert, restore, amend, and reflog to safely undo commits, recover files, and fix Git mistakes.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Almost every Git user eventually runs into the same problem.&lt;/p&gt;
&lt;p&gt;A commit was created.&lt;/p&gt;
&lt;p&gt;Then regret arrived.&lt;/p&gt;
&lt;p&gt;Maybe the commit contains broken code. Maybe the wrong file slipped in. Maybe the commit message is terrible. Or maybe the commit was already pushed before anyone noticed the mistake.&lt;/p&gt;
&lt;p&gt;At that point, people usually search:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How do I undo a Git commit?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./images/undo-git-commit.png&quot; alt=&quot;Undo Git Commit&quot; title=&quot;Undo Git Commit&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then the confusion begins.&lt;/p&gt;
&lt;p&gt;Some tutorials recommend &lt;code&gt;git reset&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Others say &lt;code&gt;git revert&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Someone suggests &lt;code&gt;checkout&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then suddenly &lt;code&gt;restore&lt;/code&gt;, &lt;code&gt;amend&lt;/code&gt;, &lt;code&gt;reflog&lt;/code&gt;, &lt;code&gt;HEAD~1&lt;/code&gt;, force push, and several unfamiliar Git concepts appear all at once.&lt;/p&gt;
&lt;p&gt;The frustrating part is that most of those answers are technically correct.&lt;/p&gt;
&lt;p&gt;The real problem is simpler:&lt;/p&gt;
&lt;p&gt;“Undoing a commit” can mean several completely different things.&lt;/p&gt;
&lt;p&gt;You might want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remove the latest commit&lt;/li&gt;
&lt;li&gt;remove a commit but keep your changes&lt;/li&gt;
&lt;li&gt;undo something already pushed&lt;/li&gt;
&lt;li&gt;restore a file&lt;/li&gt;
&lt;li&gt;rewrite the previous commit&lt;/li&gt;
&lt;li&gt;recover deleted history&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git provides different tools for each situation.&lt;/p&gt;
&lt;p&gt;Once you understand that distinction, the command overload becomes much easier to understand.&lt;/p&gt;
&lt;h2&gt;What a Commit Actually Is&lt;/h2&gt;
&lt;p&gt;A Git commit is simply a saved snapshot of your project at a specific point in time.&lt;/p&gt;
&lt;p&gt;If you&apos;ve played video games, think about save points.&lt;/p&gt;
&lt;p&gt;You reach a stable point, save progress, and continue experimenting.&lt;/p&gt;
&lt;p&gt;Git works in a similar way.&lt;/p&gt;
&lt;p&gt;A commit stores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;project state&lt;/li&gt;
&lt;li&gt;file changes&lt;/li&gt;
&lt;li&gt;author information&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;li&gt;commit message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Commits are then connected together into a history graph.&lt;/p&gt;
&lt;h2&gt;Why “Undo Commit” Causes So Much Confusion&lt;/h2&gt;
&lt;p&gt;This phrase sounds simple, but it hides several very different intentions.&lt;/p&gt;
&lt;p&gt;Sometimes you want to remove the commit &lt;strong&gt;without touching your code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes you want to remove &lt;strong&gt;both the commit and the changes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes you should not remove anything at all — you only need to create a new commit that cancels an earlier one.&lt;/p&gt;
&lt;p&gt;Different problem.&lt;/p&gt;
&lt;p&gt;Different command.&lt;/p&gt;
&lt;p&gt;That is why Git offers multiple approaches instead of a single universal &quot;undo&quot; button.&lt;/p&gt;
&lt;h2&gt;The Fast Cheat Sheet&lt;/h2&gt;
&lt;p&gt;If you want the shortest possible version:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Remove local commit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git reset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undo pushed commit safely&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git revert&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git restore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fix last commit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git commit --amend&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recover deleted history&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git reflog&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Now let’s break down what each command actually does.&lt;/p&gt;
&lt;h2&gt;Remove the Last Local Commit with &lt;code&gt;git reset&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Suppose you just created a commit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -m &quot;Temporary experiment&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few seconds later you realize:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That commit should not exist.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This removes the commit from history but keeps your file changes.&lt;/p&gt;
&lt;p&gt;By default, Git also unstages your files.&lt;/p&gt;
&lt;h3&gt;Understanding &lt;code&gt;HEAD~1&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;HEAD&lt;/code&gt; means:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the commit you are currently on&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;HEAD~1&lt;/code&gt; means:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;one commit earlier&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HEAD~1
HEAD~2
HEAD~3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;move backward through history.&lt;/p&gt;
&lt;h3&gt;Soft Reset&lt;/h3&gt;
&lt;p&gt;If you want to remove the commit but keep staging intact:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --soft HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file changes&lt;/li&gt;
&lt;li&gt;staged files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Useful when you only want to rewrite the commit message or slightly reorganize the commit.&lt;/p&gt;
&lt;h3&gt;Hard Reset&lt;/h3&gt;
&lt;p&gt;The dangerous version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This removes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the commit&lt;/li&gt;
&lt;li&gt;working directory changes&lt;/li&gt;
&lt;li&gt;staged files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your repository becomes identical to the previous commit.&lt;/p&gt;
&lt;p&gt;Use this command carefully.&lt;/p&gt;
&lt;h2&gt;Undo a Pushed Commit with &lt;code&gt;git revert&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Once code has already been pushed, &lt;code&gt;reset&lt;/code&gt; becomes risky.&lt;/p&gt;
&lt;p&gt;History rewriting can break collaborators.&lt;/p&gt;
&lt;p&gt;That is why Git provides:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git revert &amp;lt;commit-hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of deleting history, Git creates a new commit that reverses the previous changes.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A → B → C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git revert C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A → B → C → D
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;D&lt;/code&gt; cancels the changes introduced by &lt;code&gt;C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This makes &lt;code&gt;revert&lt;/code&gt; the safest option for shared repositories.&lt;/p&gt;
&lt;h2&gt;Restore a Single File with &lt;code&gt;git restore&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes the entire commit is fine.&lt;/p&gt;
&lt;p&gt;Only one file is wrong.&lt;/p&gt;
&lt;p&gt;Imagine you broke:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restore it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git replaces the file with the version from the latest commit.&lt;/p&gt;
&lt;p&gt;You can also restore from a specific commit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore --source=HEAD~1 config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore --source=a1b2c3d config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is extremely useful when recovering earlier implementations.&lt;/p&gt;
&lt;h2&gt;Fix the Last Commit with &lt;code&gt;git commit --amend&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes the commit is mostly correct.&lt;/p&gt;
&lt;p&gt;You simply forgot something.&lt;/p&gt;
&lt;p&gt;Common cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;missing file&lt;/li&gt;
&lt;li&gt;bad commit message&lt;/li&gt;
&lt;li&gt;small bug fix&lt;/li&gt;
&lt;li&gt;forgotten config change&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git opens the commit editor.&lt;/p&gt;
&lt;p&gt;Update the message.&lt;/p&gt;
&lt;p&gt;Save.&lt;/p&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;p&gt;If you forgot a file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add missing-file.ts
git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git rebuilds the previous commit as if it had originally been created correctly.&lt;/p&gt;
&lt;h3&gt;Important Warning&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;amend&lt;/code&gt; rewrites history.&lt;/p&gt;
&lt;p&gt;If the commit was already pushed, you may need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push --force
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use force push carefully.&lt;/p&gt;
&lt;h2&gt;Recover Deleted Work with &lt;code&gt;git reflog&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This command rescues many Git disasters.&lt;/p&gt;
&lt;p&gt;Especially after accidental:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reflog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;7f2a3c1 HEAD@{0}: reset: moving to HEAD~1
3b9d221 HEAD@{1}: commit: Add payment logic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice something important.&lt;/p&gt;
&lt;p&gt;Your &quot;deleted&quot; commit is usually still recoverable.&lt;/p&gt;
&lt;p&gt;Grab the hash:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard 3b9d221
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Repository restored.&lt;/p&gt;
&lt;p&gt;Panic avoided.&lt;/p&gt;
&lt;h2&gt;Undo &lt;code&gt;git add&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Technically not a commit operation.&lt;/p&gt;
&lt;p&gt;Still useful.&lt;/p&gt;
&lt;p&gt;If you staged the wrong file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unstage it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore --staged package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file remains modified.&lt;/p&gt;
&lt;p&gt;Git simply removes it from staging.&lt;/p&gt;
&lt;h2&gt;Which Command Should You Use?&lt;/h2&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when removing local commits.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git revert
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when undoing pushed commits.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;for recovering files.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;for repairing recent commits.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git…</content:encoded></item><item><title>GPT Shortcuts That Actually Work</title><link>https://jsdev.space/gpt-shortcuts-that-work/</link><guid isPermaLink="true">https://jsdev.space/gpt-shortcuts-that-work/</guid><description>Learn which GPT shortcuts genuinely improve responses, which are unreliable, and which “secret commands” are complete myths.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TikTok, YouTube, Reddit, and Telegram are full of so-called “secret GPT commands.”&lt;/p&gt;
&lt;p&gt;People constantly share prompts like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IQ200
/UNFILTER
X10THINK
/RAW
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually claiming they unlock hidden reasoning modes, bypass restrictions, or somehow make the model dramatically smarter.&lt;/p&gt;
&lt;p&gt;Most of these claims are exaggerated or completely false.&lt;/p&gt;
&lt;p&gt;There is no hidden “superintelligence mode.” No secret admin console. No magical phrase that suddenly transforms a language model into something fundamentally different.&lt;/p&gt;
&lt;p&gt;At the same time, some GPT shortcuts really are useful. Not because they unlock hidden systems, but because they communicate intent more clearly and efficiently.&lt;/p&gt;
&lt;p&gt;That distinction matters.&lt;/p&gt;
&lt;p&gt;This article breaks common GPT shortcuts into three groups:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;shortcuts that genuinely help&lt;/li&gt;
&lt;li&gt;shortcuts that work inconsistently&lt;/li&gt;
&lt;li&gt;shortcuts that are basically internet mythology&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not reverse engineering AI models. It is understanding which prompt patterns actually improve day-to-day work with language models.&lt;/p&gt;
&lt;h2&gt;Why GPT Shortcuts Sometimes Work&lt;/h2&gt;
&lt;p&gt;One of the biggest misconceptions about ChatGPT is the idea that it behaves like a terminal or command interpreter.&lt;/p&gt;
&lt;p&gt;It does not.&lt;/p&gt;
&lt;p&gt;When someone writes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/REDTEAM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the model is not switching into a hidden mode.&lt;/p&gt;
&lt;p&gt;Instead, it recognizes a familiar pattern from its training data and continues the conversation in a style associated with that pattern.&lt;/p&gt;
&lt;p&gt;That is an important difference.&lt;/p&gt;
&lt;p&gt;GPT shortcuts are essentially compact prompt instructions.&lt;/p&gt;
&lt;p&gt;Some work well because they clearly describe the desired tone, structure, or behavior in a very short format.&lt;/p&gt;
&lt;p&gt;Others fail because they are vague internet myths with no meaningful instruction behind them.&lt;/p&gt;
&lt;p&gt;Another important detail is that model behavior is probabilistic.&lt;/p&gt;
&lt;p&gt;A shortcut might work perfectly ten times in a row and then suddenly get ignored on the next attempt.&lt;/p&gt;
&lt;p&gt;That inconsistency is normal.&lt;/p&gt;
&lt;h2&gt;EL5 Is Still One of the Most Useful Shortcuts&lt;/h2&gt;
&lt;p&gt;The classic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;stands for:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Explain Like I&apos;m 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This remains one of the most reliable shortcuts because it strongly influences how the model structures information.&lt;/p&gt;
&lt;p&gt;Responses usually become:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simpler&lt;/li&gt;
&lt;li&gt;shorter&lt;/li&gt;
&lt;li&gt;more conversational&lt;/li&gt;
&lt;li&gt;example-driven&lt;/li&gt;
&lt;li&gt;less technical&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL5: Explain Docker containers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will usually produce analogies and simplified explanations instead of implementation details and infrastructure terminology.&lt;/p&gt;
&lt;p&gt;For onboarding, teaching, and documentation, this shortcut is genuinely useful.&lt;/p&gt;
&lt;h2&gt;EL10 and EL15 Are Less Predictable&lt;/h2&gt;
&lt;p&gt;Variants like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL10
EL15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;try to simulate different levels of understanding.&lt;/p&gt;
&lt;p&gt;Sometimes they work reasonably well.&lt;/p&gt;
&lt;p&gt;Sometimes they barely change the output.&lt;/p&gt;
&lt;p&gt;The problem is that language models do not maintain a strict internal “age scale” for explanations.&lt;/p&gt;
&lt;p&gt;If the goal is more advanced output, direct instructions are usually more effective.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Explain this like you&apos;re speaking to a senior backend engineer.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is typically more reliable than:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which often changes very little.&lt;/p&gt;
&lt;h2&gt;/STEP-BY-STEP Is Genuinely Helpful&lt;/h2&gt;
&lt;p&gt;One of the most practical shortcuts is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/STEP-BY-STEP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This encourages the model to break problems into stages instead of jumping directly to conclusions.&lt;/p&gt;
&lt;p&gt;It works especially well for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;programming&lt;/li&gt;
&lt;li&gt;debugging&lt;/li&gt;
&lt;li&gt;mathematics&lt;/li&gt;
&lt;li&gt;architecture discussions&lt;/li&gt;
&lt;li&gt;learning workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/STEP-BY-STEP Debug this React hydration error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually produces a much clearer and more structured response than a generic debugging request.&lt;/p&gt;
&lt;p&gt;This shortcut often improves reasoning visibility significantly.&lt;/p&gt;
&lt;h2&gt;/REDTEAM Produces Better Criticism&lt;/h2&gt;
&lt;p&gt;Another genuinely useful shortcut is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/REDTEAM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shifts the model toward critique instead of agreement.&lt;/p&gt;
&lt;p&gt;The response becomes more skeptical, risk-focused, and analytical.&lt;/p&gt;
&lt;p&gt;Useful scenarios include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API security reviews&lt;/li&gt;
&lt;li&gt;architecture analysis&lt;/li&gt;
&lt;li&gt;business risks&lt;/li&gt;
&lt;li&gt;performance bottlenecks&lt;/li&gt;
&lt;li&gt;edge-case evaluation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/REDTEAM Analyze security problems in this authentication flow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In many cases, this produces much more valuable feedback than standard prompting.&lt;/p&gt;
&lt;h2&gt;/HUMAN Can Improve Tone&lt;/h2&gt;
&lt;p&gt;One of the most common complaints about AI-generated writing is that it sounds robotic.&lt;/p&gt;
&lt;p&gt;The shortcut:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/HUMAN
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sometimes helps reduce that problem.&lt;/p&gt;
&lt;p&gt;It tends to push responses toward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more conversational language&lt;/li&gt;
&lt;li&gt;shorter sentences&lt;/li&gt;
&lt;li&gt;softer transitions&lt;/li&gt;
&lt;li&gt;less corporate phrasing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It does not magically produce perfect human writing, but it can noticeably improve tone in casual content.&lt;/p&gt;
&lt;h2&gt;Formatting Shortcuts Are Usually Reliable&lt;/h2&gt;
&lt;p&gt;Formatting commands tend to work better than abstract “thinking mode” prompts because they clearly define the expected output structure.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/BULLET
/TABLE
/CONCISE
/DETAIL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These shortcuts are practical because they solve real formatting problems.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/TABLE Compare React, Vue, and Svelte
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually generates a clean comparison table immediately.&lt;/p&gt;
&lt;p&gt;Similarly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/CONCISE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;often reduces filler and shortens explanations.&lt;/p&gt;
&lt;p&gt;While:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/DETAIL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;typically increases elaboration and context depth.&lt;/p&gt;
&lt;p&gt;These are among the most useful prompt shortcuts for everyday work.&lt;/p&gt;
&lt;h2&gt;/CODE Is Useful for Development Work&lt;/h2&gt;
&lt;p&gt;Another practical shortcut is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/CODE python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/CODE js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This usually shifts the response toward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more implementation&lt;/li&gt;
&lt;li&gt;less explanation&lt;/li&gt;
&lt;li&gt;cleaner code-focused output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is especially useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prototyping&lt;/li&gt;
&lt;li&gt;boilerplate generation&lt;/li&gt;
&lt;li&gt;quick scripting&lt;/li&gt;
&lt;li&gt;debugging snippets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Most “Thinking Mode” Commands Are Overhyped&lt;/h2&gt;
&lt;p&gt;This is where things become much less impressive.&lt;/p&gt;
&lt;p&gt;Commands like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X10THINK
X5THINK
IQ200
IQ150
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;are mostly placebo.&lt;/p&gt;
&lt;p&gt;People often believe these prompts activate hidden reasoning systems or force the model to “think harder.”&lt;/p&gt;
&lt;p&gt;That is not really what happens.&lt;/p&gt;
&lt;p&gt;In practice, the model usually just:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;writes longer responses&lt;/li&gt;
&lt;li&gt;uses more complicated language&lt;/li&gt;
&lt;li&gt;sounds more dramatic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no hidden intelligence switch.&lt;/p&gt;
&lt;p&gt;Typing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IQ200
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;does not suddenly make the model more intelligent.&lt;/p&gt;
&lt;p&gt;The output may sound more sophisticated, but sounding smarter and reasoning better are not the same thing.&lt;/p&gt;
&lt;h2&gt;/UNFILTER Is Mostly Internet Mythology&lt;/h2&gt;
&lt;p&gt;Probably the most famous fake shortcut is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/UNFILTER
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;People constantly claim it disables moderation systems or removes model restrictions.&lt;/p&gt;
&lt;p&gt;It does not.&lt;/p&gt;
&lt;p&gt;Modern language models do not expose a secret “disable safety” switch through prompts.&lt;/p&gt;
&lt;p&gt;At best, the model may slightly change tone or formatting.&lt;/p&gt;
&lt;p&gt;In most cases, nothing meaningful happens.&lt;/p&gt;
&lt;p&gt;The same applies to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/RAW
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which supposedly reveals hidden prompts, internal state, o…</content:encoded></item><item><title>Best React Libraries and Tools in 2026</title><link>https://jsdev.space/react-stack-2026/</link><guid isPermaLink="true">https://jsdev.space/react-stack-2026/</guid><description>A complete guide to the modern React ecosystem in 2026, covering frameworks, forms, state management, UI libraries, testing, and developer tooling.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;The Modern React Stack Explained for 2026&lt;/h2&gt;
&lt;p&gt;The React ecosystem changes faster than almost any other frontend ecosystem.&lt;/p&gt;
&lt;p&gt;A few years ago, most projects looked nearly identical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create React App&lt;/li&gt;
&lt;li&gt;Redux&lt;/li&gt;
&lt;li&gt;Material UI&lt;/li&gt;
&lt;li&gt;Formik&lt;/li&gt;
&lt;li&gt;styled-components&lt;/li&gt;
&lt;li&gt;webpack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Today, very few new React applications are built that way.&lt;/p&gt;
&lt;p&gt;Modern React development in 2026 is shaped by several major shifts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server-first rendering,&lt;/li&gt;
&lt;li&gt;TypeScript-first APIs,&lt;/li&gt;
&lt;li&gt;edge-ready runtimes,&lt;/li&gt;
&lt;li&gt;monorepo tooling,&lt;/li&gt;
&lt;li&gt;async React architecture,&lt;/li&gt;
&lt;li&gt;AI-assisted workflows,&lt;/li&gt;
&lt;li&gt;and significantly better developer experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting a new React project today can feel overwhelming because there are now dozens of excellent options in almost every category.&lt;/p&gt;
&lt;p&gt;Should you use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt; or &lt;a href=&quot;https://tanstack.com/start&quot;&gt;TanStack Start&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pmndrs/zustand&quot;&gt;Zustand&lt;/a&gt; or &lt;a href=&quot;https://github.com/pmndrs/jotai&quot;&gt;Jotai&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react-hook-form.com/&quot;&gt;React Hook Form&lt;/a&gt; or &lt;a href=&quot;https://tanstack.com/form&quot;&gt;TanStack Form&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot;&gt;shadcn/ui&lt;/a&gt; or &lt;a href=&quot;https://mantine.dev/&quot;&gt;Mantine&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; or &lt;a href=&quot;https://panda-css.com/&quot;&gt;Panda CSS&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vitest.dev/&quot;&gt;Vitest&lt;/a&gt; or &lt;a href=&quot;https://jestjs.io/&quot;&gt;Jest&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://better-auth.com/&quot;&gt;Better Auth&lt;/a&gt; or &lt;a href=&quot;https://authjs.dev/&quot;&gt;Auth.js&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answer depends heavily on what kind of product you are building.&lt;/p&gt;
&lt;p&gt;A SaaS dashboard has completely different requirements than:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a marketing site,&lt;/li&gt;
&lt;li&gt;a collaborative editor,&lt;/li&gt;
&lt;li&gt;an e-commerce storefront,&lt;/li&gt;
&lt;li&gt;a browser-based design tool,&lt;/li&gt;
&lt;li&gt;or a real-time analytics application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide breaks down the modern React stack category by category and explains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which tools became the new defaults,&lt;/li&gt;
&lt;li&gt;which libraries are losing momentum,&lt;/li&gt;
&lt;li&gt;which alternatives still make sense,&lt;/li&gt;
&lt;li&gt;and what tradeoffs come with every major decision.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not to create a universal ranking.&lt;/p&gt;
&lt;p&gt;Instead, the goal is to help developers understand how the React ecosystem actually looks in 2026.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React Ecosystem Trends in 2026&lt;/h2&gt;
&lt;p&gt;Before comparing libraries individually, it is important to understand the larger architectural trends shaping React development.&lt;/p&gt;
&lt;p&gt;The React ecosystem in 2026 is no longer centered around building purely client-side applications.&lt;/p&gt;
&lt;p&gt;Modern React applications increasingly focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server-first rendering,&lt;/li&gt;
&lt;li&gt;streaming interfaces,&lt;/li&gt;
&lt;li&gt;async workflows,&lt;/li&gt;
&lt;li&gt;edge execution,&lt;/li&gt;
&lt;li&gt;AI-assisted experiences,&lt;/li&gt;
&lt;li&gt;and shared type-safe contracts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Several ecosystem-wide changes influenced nearly every major library:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trend&lt;/th&gt;
&lt;th&gt;Impact on the Ecosystem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React Server Components&lt;/td&gt;
&lt;td&gt;Reduced client bundle sizes and moved data fetching closer to the server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript Everywhere&lt;/td&gt;
&lt;td&gt;Libraries increasingly design APIs around type inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-Assisted Development&lt;/td&gt;
&lt;td&gt;Tooling shifted toward faster iteration and code generation workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge Deployment&lt;/td&gt;
&lt;td&gt;Frameworks optimized for distributed execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monorepo Growth&lt;/td&gt;
&lt;td&gt;Shared packages and workspace tooling became standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utility-First Styling&lt;/td&gt;
&lt;td&gt;Tailwind and token-based systems became dominant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async UI Patterns&lt;/td&gt;
&lt;td&gt;Streaming and optimistic interfaces became common&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These shifts explain why many older React patterns no longer feel modern.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;heavy Redux architectures became less common,&lt;/li&gt;
&lt;li&gt;large runtime CSS-in-JS systems lost momentum,&lt;/li&gt;
&lt;li&gt;and traditional REST-heavy frontend architectures increasingly moved toward server actions and typed APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding these broader shifts makes it much easier to choose tools intentionally instead of simply following trends.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Visual Overview of a Modern React Stack&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;┌────────────────────────────────────────────┐
│                 Frontend                   │
├────────────────────────────────────────────┤
│ Next.js / TanStack Start / Remix           │
│ Tailwind CSS / shadcn/ui                   │
│ React Hook Form / TanStack Form            │
│ Zustand / Jotai                            │
│ TanStack Query                             │
└────────────────────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────┐
│              Shared Contracts              │
├────────────────────────────────────────────┤
│ TypeScript Types                           │
│ Zod Schemas                                │
│ API Contracts                              │
└────────────────────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────┐
│                 Backend                    │
├────────────────────────────────────────────┤
│ Node.js / NestJS / Hono                    │
│ Drizzle ORM / Prisma                       │
│ PostgreSQL                                 │
│ Better Auth                                │
└────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This architecture became extremely common because it balances:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;developer experience,&lt;/li&gt;
&lt;li&gt;scalability,&lt;/li&gt;
&lt;li&gt;maintainability,&lt;/li&gt;
&lt;li&gt;and type safety.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Frameworks and Meta Frameworks&lt;/h3&gt;
&lt;h4&gt;Next.js&lt;/h4&gt;
&lt;p&gt;Next.js is still the dominant React framework in 2026.&lt;/p&gt;
&lt;p&gt;Its biggest advantage is ecosystem gravity.&lt;/p&gt;
&lt;p&gt;You get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React Server Components,&lt;/li&gt;
&lt;li&gt;streaming,&lt;/li&gt;
&lt;li&gt;route handlers,&lt;/li&gt;
&lt;li&gt;server actions,&lt;/li&gt;
&lt;li&gt;edge support,&lt;/li&gt;
&lt;li&gt;image optimization,&lt;/li&gt;
&lt;li&gt;metadata handling,&lt;/li&gt;
&lt;li&gt;and deployment integration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The App Router has matured significantly compared to the early React 18 transition period.&lt;/p&gt;
&lt;p&gt;Modern Next.js applications usually combine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server components for data-heavy pages,&lt;/li&gt;
&lt;li&gt;client components only where interactivity is needed,&lt;/li&gt;
&lt;li&gt;and server actions for mutations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A typical modern Next.js stack often includes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default async function DashboardPage() {
  const projects = await getProjects();

  return (
    &amp;lt;DashboardLayout&amp;gt;
      &amp;lt;ProjectList projects={projects} /&amp;gt;
    &amp;lt;/DashboardLayout&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next.js is usually the safest choice for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SaaS products,&lt;/li&gt;
&lt;li&gt;content platforms,&lt;/li&gt;
&lt;li&gt;dashboards,&lt;/li&gt;
&lt;li&gt;e-commerce,&lt;/li&gt;
&lt;li&gt;and hybrid server/client applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;TanStack Start&lt;/h3&gt;
&lt;p&gt;TanStack Start became one of the most interesting alternatives to Next.js.&lt;/p&gt;
&lt;p&gt;Instead of focusing heavily on conventions, it focuses on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;type safety,&lt;/li&gt;
&lt;li&gt;route-based data loading,&lt;/li&gt;
&lt;li&gt;modern caching,&lt;/li&gt;
&lt;li&gt;and a highly composable architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Developers who already use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TanStack Router,&lt;/li&gt;
&lt;li&gt;TanStack Query,&lt;/li&gt;
&lt;li&gt;TanStack Form,&lt;/li&gt;
&lt;li&gt;or TanStack Table&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;usually feel very comfortable inside the TanStack ecosystem.&lt;/p&gt;
&lt;p&gt;TanStack Start especially shines in applications that require:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;advanced client-side routing,&lt;/li&gt;
&lt;li&gt;granular data fetching control,&lt;/li&gt;
&lt;li&gt;optimistic updates,&lt;/li&gt;
&lt;li&gt;and complex dashboard workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Remix&lt;/h3&gt;
&lt;p&gt;Remix still has one of the cleanest mental models for full-stack React.&lt;/p&gt;
&lt;p…</content:encoded></item><item><title>Friday Links #37 — JavaScript Trends &amp; Tools</title><link>https://jsdev.space/friday/friday-37/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-37/</guid><description>A curated roundup of the latest JavaScript releases, tools, AI updates, and performance benchmarks to keep developers ahead this week.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-37.png&quot; alt=&quot;Friday Links #37&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The JavaScript ecosystem doesn’t slow down—it evolves weekly. Friday Links #37 distills the most important updates across libraries, runtimes, tooling, and AI-driven development. From new releases pushing performance boundaries to emerging tools that simplify workflows, this issue highlights what’s worth your attention. Whether you’re building production systems or exploring new stacks, these curated picks help you stay informed without the noise.&lt;/p&gt;
&lt;h2&gt;🧠 Language &amp;amp; Runtime Updates&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Node.js 26 (Current) — almost here&lt;/strong&gt;
The upcoming release of Node.js 26 is expected to ship with V8 14.6 and enable the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal&quot;&gt;Temporal API&lt;/a&gt; by default—a major step toward handling dates natively without relying on third-party libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JavaScript (ES2025 → ES2026)&lt;/strong&gt;
The language continues evolving toward ES2026, with ongoing improvements to the standard library and deeper integration with AI-driven tooling and workflows.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://deno.com/blog/fresh-2.3&quot;&gt;🍋 Fresh 2.3&lt;/a&gt; — the Deno-native full-stack framework — introduces first-class WebSocket support, eliminates unnecessary JavaScript for pages that don’t need it, and makes the View Transitions API effortless with a single attribute in your views.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://piccalil.li/blog/the-end-of-responsive-images/&quot;&gt;The end of responsive images&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/articles/baseline-in-action-dialog-popover&quot;&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; and popover: Baseline layered UI patterns&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.joshwcomeau.com/animation/scroll-driven-animations/&quot;&gt;Scroll-Driven Animations&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.debugbear.com/blog/lazy-loading-performance&quot;&gt;How to Use Lazy Loading Without Hurting Web Performance&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nik.digital/posts/compositing-blending&quot;&gt;Compositing &amp;amp; Blending&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/css-multi-column-layout-wrapping-features/&quot;&gt;Looking at New CSS Multi-Column Layout Wrapping Features&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/recreating-apples-vision-pro-animation-in-css/&quot;&gt;Recreating Apple’s Vision Pro Animation in CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/constructable-stylesheets-and-adoptedstylesheets-one-parse-every-shadow-root/&quot;&gt;Constructable Stylesheets and adoptedStyleSheets: One Parse, Every Shadow Root&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nerdy.dev/CSS-recently-in-all-browsers&quot;&gt;CSS Recently In All Browsers&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2026/04/session-timeouts-accessibility-barrier-authentication-design/&quot;&gt;Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://saschb2b.com/blog/react-compiler-year-in-review&quot;&gt;The React Compiler at Eighteen Months: The Arc, the Debates, and What&apos;s Next&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://shadcnstudio.com/blog/migrate-from-radix-ui-to-base-ui&quot;&gt;Migrating from Radix UI to Base UI: Step-by-Step Guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://csswizardry.com/2026/04/font-family-doesnt-fall-back-the-way-you-think/&quot;&gt;font-family Doesn’t Fall Back the Way You Think&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.isquaredsoftware.com/presentations/2026-04-react-compiler-rendering/&quot;&gt;A Guide to React Compiler Rendering&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;TypeScript 7.0 Beta — gaining traction&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-beta/&quot;&gt;TypeScript 7&lt;/a&gt; is generating significant discussion. It introduces a native compiler written in Go, promising substantial performance gains and a redesigned architecture for large-scale projects.&lt;/p&gt;
&lt;p&gt;TypeScript 6 — a transitional release
TypeScript 6.0 acts as a bridge toward TS 7, bringing:&lt;/p&gt;
&lt;p&gt;Deprecation of legacy APIs
Updated DOM typings
Adoption of modern &lt;code&gt;import attributes&lt;/code&gt; (&lt;code&gt;with&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/perry.png&quot; alt=&quot;&amp;quot;Perry TypeScript Compiler&amp;quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.perryts.com/&quot;&gt;Perry&lt;/a&gt; is a cross-platform TypeScript compiler that compiles code directly into native executables, offering an alternative to traditional JavaScript runtimes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://openwarp.zerx.dev/en/&quot;&gt;OpenWarp&lt;/a&gt; - Bring any AI model into your terminal&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/view-transitions-mock&quot;&gt;View Transitions Mock&lt;/a&gt; — a non-visual polyfill for the View Transitions API — provides a JavaScript implementation of same-document transitions without the visual layer. You can write a single code path for all browsers: supported ones render transitions, while others fall back to a DOM swap, with the same promises and API behavior.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google-labs-code/design.md&quot;&gt;DESIGN.md&lt;/a&gt; is a proposed format for describing a product’s visual identity—colors, typography, spacing, and UI rules—in a structured, machine-readable way. It’s designed for AI agents and tooling, enabling them to generate consistent interfaces, designs, or code based on a single source of truth.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/open-circle/formisch&quot;&gt;Formisch&lt;/a&gt; — a schema-based, headless form library for JavaScript frameworks that handles form state and validation. It’s type-safe, fast by default, and keeps bundle size small thanks to its modular design.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://franktisellano.github.io/datatype/&quot;&gt;Datatype&lt;/a&gt; is a variable font that turns text into charts, providing a unique way to visualize data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/0xGF/boneyard&quot;&gt;Boneyard&lt;/a&gt; — generates pixel-perfect skeleton loading screens directly from your real UI, with no manual measurements or placeholder tuning.&lt;/p&gt;
&lt;p&gt;Works across frameworks including React, Preact, Vue, Svelte 5, Angular, and React Native.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/isaac-mason/crashcat&quot;&gt;crashcat&lt;/a&gt; — a JavaScript physics engine built for games, simulations, and creative web projects. It includes rigid body simulation, support for multiple shape types, continuous collision detection, and more for building dynamic, interactive experiences.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/harshankur/officeParser&quot;&gt;officeParser&lt;/a&gt; — a robust, strictly typed library for Node.js and the browser that parses office documents into a clean, hierarchical AST. It preserves rich metadata, text formatting, and supports embedded attachments.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/maximhq/bifrost&quot;&gt;Bifrost&lt;/a&gt; — a high-performance AI gateway that unifies access to 15+ providers (including OpenAI, Anthropic, Amazon Web Services, and Google) through a single OpenAI-compatible API.&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel-labs/portless&quot;&gt;Portless&lt;/a&gt; lets you replace port-based URLs with clean, stable local domains—so instead of &lt;code&gt;http://localhost:3000&lt;/code&gt;, you can use something like &lt;code&gt;https://myapp.localhost&lt;/code&gt;. It’s built on Node.js and now adds new features for Tailscale users.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/russellromney/honker&quot;&gt;Honker&lt;/a&gt; brings PostgreSQL-style NOTIFY/LISTEN semantics to SQLite—no daemon required. The honker-node package adds support for Node.js, making it easy to integrate event-driven messaging into your apps.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Secreto31126/whatsapp-api-js&quot;&gt;whatsapp-api-js&lt;/a&gt; — a lightweight, dependency-free library for interacting with the WhatsApp Cloud API, built for efficiency and full TypeScript support.&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pnpm.io/blog/releases/11.0&quot;&gt;pnpm 11.0 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/avajs/ava/releases/tag/v8.0.0&quot;&gt;AVA 8.0&lt;/a&gt; — the popular Node.js test runner is now fully ESM and introduces two new test modifiers: &lt;code&gt;test.skipIf()&lt;/code&gt; and &lt;code&gt;test.runIf()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/metafloor/bwip-js&quot;&gt;BWIP-JS 4.10&lt;/a&gt; — a pure JavaScript port of the origina…</content:encoded></item><item><title>Howto Use IMask.js for Input Masking in JavaScript Forms</title><link>https://jsdev.space/howto/imaskjs-input-masking/</link><guid isPermaLink="true">https://jsdev.space/howto/imaskjs-input-masking/</guid><description>Learn how to implement input masking for cards, dates, currency, and more using IMask.js with real-world examples and best practices.</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Form handling is one of the most underestimated parts of frontend development.&lt;/p&gt;
&lt;p&gt;It looks simple: just a few inputs, maybe a submit button. But in reality, forms are where most user frustration happens. Incorrect formats, unclear validation rules, and inconsistent input behavior all lead to errors, abandoned flows, and bad data.&lt;/p&gt;
&lt;p&gt;Input masking solves this at the source.&lt;/p&gt;
&lt;p&gt;Instead of reacting to bad input after submission, you guide the user while they type.&lt;/p&gt;
&lt;p&gt;In this article, we’ll go deep into IMask.js — a lightweight, flexible library that allows you to control input formatting in real time. This is not just a basic tutorial. You’ll learn patterns, edge cases, and production-ready practices.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Input Masking Is a Core UX Layer&lt;/h2&gt;
&lt;p&gt;Without masking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;users guess formats&lt;/li&gt;
&lt;li&gt;validation fails often&lt;/li&gt;
&lt;li&gt;error messages increase friction&lt;/li&gt;
&lt;li&gt;backend receives inconsistent data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With masking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input is guided instantly&lt;/li&gt;
&lt;li&gt;errors are prevented early&lt;/li&gt;
&lt;li&gt;formatting becomes predictable&lt;/li&gt;
&lt;li&gt;UX feels faster and more professional&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Masking is not just visual formatting — it&apos;s a &lt;strong&gt;data normalization layer at the UI level&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What Makes IMask.js Different&lt;/h2&gt;
&lt;p&gt;IMask.js stands out because it combines flexibility with performance.&lt;/p&gt;
&lt;p&gt;Key characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;no dependencies&lt;/li&gt;
&lt;li&gt;small bundle (~15kb gzipped)&lt;/li&gt;
&lt;li&gt;works with plain JS and frameworks&lt;/li&gt;
&lt;li&gt;supports multiple mask types&lt;/li&gt;
&lt;li&gt;allows custom logic via functions&lt;/li&gt;
&lt;li&gt;handles mobile input well&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unlike many libraries, IMask doesn’t lock you into one approach. It gives you primitives to build your own behavior.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pnpm add imask
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import MaskCore from &quot;imask&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Mental Model of IMask&lt;/h2&gt;
&lt;p&gt;Before writing code, understand how IMask works internally.&lt;/p&gt;
&lt;p&gt;There are three key concepts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Input Element&lt;/strong&gt; – the DOM node&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mask Configuration&lt;/strong&gt; – rules defining formatting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mask Instance&lt;/strong&gt; – runtime controller&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const inputNode = document.querySelector(&quot;#phone&quot;)

const maskController = MaskCore(inputNode, {
  mask: &quot;+{1} (000) 000-0000&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The instance acts as a bridge between raw input and formatted output.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Basic Example: Phone Input&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input id=&quot;contact-phone&quot; placeholder=&quot;+1 (___) ___-____&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;const phoneNode = document.querySelector(&quot;#contact-phone&quot;)

const phoneController = MaskCore(phoneNode, {
  mask: &quot;+{1} (000) 000-0000&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the user types, formatting is applied automatically.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Static Masks (Fixed Structure)&lt;/h2&gt;
&lt;p&gt;Use static masks when format is strictly defined.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const cardNode = document.querySelector(&quot;#card&quot;)

const cardController = MaskCore(cardNode, {
  mask: &quot;0000 0000 0000 0000&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Best for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;credit cards&lt;/li&gt;
&lt;li&gt;IDs&lt;/li&gt;
&lt;li&gt;formatted codes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Dynamic Masks (Multiple Formats)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const phoneDynamic = MaskCore(document.querySelector(&quot;#phone&quot;), {
  mask: [
    { mask: &quot;+{1} (000) 000-0000&quot; },
    { mask: &quot;+{44} 0000 000000&quot; }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IMask automatically selects the correct pattern based on input.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Number Mask (Currency &amp;amp; Prices)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const amountNode = document.querySelector(&quot;#amount&quot;)

const currencyController = MaskCore(amountNode, {
  mask: Number,
  min: 0,
  max: 1000000,
  thousandsSeparator: &quot;,&quot;,
  radix: &quot;.&quot;,
  precision: 2,
  prefix: &quot;$ &quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;decimal precision&lt;/li&gt;
&lt;li&gt;limits&lt;/li&gt;
&lt;li&gt;separators&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Date Mask&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const dateNode = document.querySelector(&quot;#date&quot;)

const dateController = MaskCore(dateNode, {
  mask: Date,
  pattern: &quot;yyyy-mm-dd&quot;,
  lazy: false
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents invalid formats like &lt;code&gt;2026-99-99&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Regex Mask&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const emailNode = document.querySelector(&quot;#email&quot;)

const emailController = MaskCore(emailNode, {
  mask: /^\S+@\S+\.\S+$/
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simple validation rules&lt;/li&gt;
&lt;li&gt;constrained input&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Function Mask (Full Control)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const evenNode = document.querySelector(&quot;#even&quot;)

const evenController = MaskCore(evenNode, {
  mask: (value) =&amp;gt; {
    const parsed = Number(value)
    return parsed % 2 === 0 ? value : value.slice(0, -1)
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach is powerful but should be used carefully.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Real-World Example: Credit Card Detection&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const ccNode = document.querySelector(&quot;#cc&quot;)

const ccController = MaskCore(ccNode, {
  mask: &quot;0000 0000 0000 0000&quot;
})

ccController.on(&quot;accept&quot;, () =&amp;gt; {
  const raw = ccController.unmaskedValue

  let type = &quot;unknown&quot;

  if (/^4/.test(raw)) type = &quot;visa&quot;
  else if (/^5[1-5]/.test(raw)) type = &quot;mastercard&quot;
  else if (/^3[47]/.test(raw)) type = &quot;amex&quot;

  console.log(type)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Events System&lt;/h2&gt;
&lt;p&gt;IMask exposes lifecycle hooks:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;maskController.on(&quot;accept&quot;, () =&amp;gt; {
  console.log(maskController.value)
})

maskController.on(&quot;complete&quot;, () =&amp;gt; {
  console.log(&quot;complete&quot;)
})

maskController.on(&quot;reject&quot;, (input) =&amp;gt; {
  console.log(&quot;rejected&quot;, input)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These allow integration with UI state, validation, and analytics.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Advanced Configuration&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;MaskCore(inputNode, {
  mask: &quot;0000-00-00&quot;,
  lazy: true,
  nullable: false,
  unmask: true,
  placeholderChar: &quot;_&quot;,
  prepare: (val) =&amp;gt; val.toUpperCase(),
  validate: (val) =&amp;gt; val.length === 10
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Important options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt; → allow partial input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unmask&lt;/code&gt; → return raw value&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prepare&lt;/code&gt; → transform input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;validate&lt;/code&gt; → enforce rules&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;React Integration&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { useEffect, useRef } from &quot;react&quot;
import MaskCore from &quot;imask&quot;

export function MaskedInput() {
  const nodeRef = useRef(null)

  useEffect(() =&amp;gt; {
    const instance = MaskCore(nodeRef.current, {
      mask: &quot;+{1} (000) 000-0000&quot;
    })

    return () =&amp;gt; instance.destroy()
  }, [])

  return &amp;lt;input ref={nodeRef} /&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Common Pitfalls&lt;/h2&gt;
&lt;h3&gt;1. Changing Value Directly&lt;/h3&gt;
&lt;p&gt;Wrong:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inputNode.value = &quot;123&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;maskController.value = &quot;123&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2. Autofill Conflicts&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input autocomplete=&quot;off&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or sync manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inputNode.addEventListener(&quot;change&quot;, () =&amp;gt; {
  maskController.updateValue()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3. Mobile Input Issues&lt;/h3&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lazy: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to reduce input jitter.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;keep masks readable&lt;/li&gt;
&lt;li&gt;avoid over-engineering regex&lt;/li&gt;
&lt;li&gt;always use unmaskedValue for backend&lt;/li&gt;
&lt;li&gt;keep commits atomic (if integrating with forms logic)&lt;/li&gt;
&lt;li&gt;test on mobile devices&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;When NOT to Use Masking&lt;/h2&gt;
&lt;p&gt;Avoid masking when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input is free text&lt;/li&gt;
&lt;li&gt;validation is business-driven (not format-driven)&lt;/li&gt;
&lt;li&gt;accessibility would suffer&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;IMask.js is not just a utility — it&apos;s a UX upgrade.&lt;/p&gt;
&lt;p&gt;It allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enforce structure at input level&lt;/li&gt;
&lt;li&gt;reduce validation complexity&lt;/li&gt;
&lt;li&gt;improve perceived performance&lt;/li&gt;
&lt;li&gt;create predictable user flows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once integrated properly, your forms stop being fragile.&lt;/p&gt;
&lt;p&gt;They become reliable systems.&lt;/p&gt;
&lt;p&gt;And in modern frontend development, that’s a huge advantage.&lt;/p…</content:encoded></item><item><title>Git for Beginners: Branches, Commits, and Your First Pull Request</title><link>https://jsdev.space/git-branches-commits-pull-request/</link><guid isPermaLink="true">https://jsdev.space/git-branches-commits-pull-request/</guid><description>A practical beginner’s guide to Git workflows. Learn how branches, commits, and pull requests work together in real development teams.</description><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Git is one of the most important tools in modern software development. Nearly every team relies on it to manage source code, collaborate, and track project history.&lt;/p&gt;
&lt;p&gt;Yet for beginners, Git often feels intimidating.&lt;/p&gt;
&lt;p&gt;Developers create branches with names like test123 or asd. Commits say things like “update”, “fix”, or even “...”. Pull requests contain dozens of unrelated files, temporary changes, and sometimes even accidental edits.&lt;/p&gt;
&lt;p&gt;And the worst part? Many developers don’t realize this is a problem.&lt;/p&gt;
&lt;p&gt;In a real team environment, sloppy Git usage quickly turns into chaos. Imagine trying to find the commit that broke the build when the history looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fix
update
changes
fix again
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good luck.&lt;/p&gt;
&lt;p&gt;Git is not just a storage system for code.
It is a communication tool between developers.&lt;/p&gt;
&lt;p&gt;In this guide, you&apos;ll learn how Git actually works and how to use it in a way that makes collaboration smooth and professional.&lt;/p&gt;
&lt;h2&gt;Git Is Not Magic — It’s Just a Graph&lt;/h2&gt;
&lt;p&gt;Before learning commands, it&apos;s important to understand what Git really stores.&lt;/p&gt;
&lt;p&gt;Git keeps project history as a graph of commits.&lt;/p&gt;
&lt;p&gt;Each commit represents a snapshot of the project at a specific moment in time.&lt;/p&gt;
&lt;p&gt;A commit contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file changes&lt;/li&gt;
&lt;li&gt;author information&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;li&gt;a unique hash&lt;/li&gt;
&lt;li&gt;a commit message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A branch is simply a pointer to a commit.&lt;/p&gt;
&lt;p&gt;When you create a new commit, the branch pointer moves forward.&lt;/p&gt;
&lt;p&gt;A simplified commit graph might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A --- B --- C (main)
       \
        D --- E (feature/auth)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./images/git-workflow.png&quot; alt=&quot;Git commit graph&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt; contains the stable project history&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feature/auth&lt;/code&gt; is a separate branch for developing a new feature&lt;/li&gt;
&lt;li&gt;once the feature is complete, it gets merged back into &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This isolation is what allows teams to work safely without interfering with each other.&lt;/p&gt;
&lt;h2&gt;Types of Git Branches&lt;/h2&gt;
&lt;p&gt;For small projects, teams often only use one main branch and a few temporary branches.&lt;/p&gt;
&lt;p&gt;But larger projects usually follow a structured workflow. One popular example is Git Flow.&lt;/p&gt;
&lt;p&gt;Git Flow defines several types of branches.&lt;/p&gt;
&lt;h3&gt;Main branches&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;main&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The production-ready branch.
Everything here should be stable and deployable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;develop&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The integration branch where new features are combined before release.&lt;/p&gt;
&lt;h3&gt;Temporary branches&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;feature/&lt;/strong&gt;*&lt;/p&gt;
&lt;p&gt;Branches used to develop new functionality.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feature/user-authentication
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;release/&lt;/strong&gt;*&lt;/p&gt;
&lt;p&gt;Used to prepare a new release.&lt;/p&gt;
&lt;p&gt;Tasks may include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bug fixes&lt;/li&gt;
&lt;li&gt;documentation updates&lt;/li&gt;
&lt;li&gt;version bumps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;hotfix/&lt;/strong&gt;*&lt;/p&gt;
&lt;p&gt;Urgent fixes applied directly to production code.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hotfix/security-patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A simpler workflow for beginners&lt;/h2&gt;
&lt;p&gt;Most teams teaching Git to newcomers use a simpler model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;main
  ├─ feature/add-login
  ├─ feature/create-api
  └─ fix/email-validation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each task gets its own branch.&lt;/p&gt;
&lt;p&gt;When the task is done, the branch is merged and deleted.&lt;/p&gt;
&lt;h2&gt;Creating Your First Branch&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/example/project.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into the project directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd project
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure your main branch is up to date:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git switch main
git pull origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new branch for your task:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git switch -c feature/add-user-model
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;creates a new branch&lt;/li&gt;
&lt;li&gt;switches to it immediately&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Older Git versions used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout -b feature/add-user-model
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both commands do the same thing.&lt;/p&gt;
&lt;h2&gt;Naming Branches Correctly&lt;/h2&gt;
&lt;p&gt;Branch naming conventions matter more than beginners think.&lt;/p&gt;
&lt;p&gt;Bad example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;branch1
new-feature
test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feature/add-user-service
bugfix/login-error
hotfix/token-expiration
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;General rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use lowercase&lt;/li&gt;
&lt;li&gt;avoid spaces&lt;/li&gt;
&lt;li&gt;use hyphens or slashes&lt;/li&gt;
&lt;li&gt;avoid non-ASCII characters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clear names help the entire team understand what the branch contains.&lt;/p&gt;
&lt;h2&gt;Making Meaningful Commits&lt;/h2&gt;
&lt;p&gt;After writing some code, the next step is creating commits.&lt;/p&gt;
&lt;p&gt;Imagine we add a simple JavaScript class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before committing, Git needs to know which files to include.&lt;/p&gt;
&lt;p&gt;Add the file to the staging area:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add src/main/project/User.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also add all changes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this is risky — temporary files might accidentally be committed.&lt;/p&gt;
&lt;p&gt;It is usually safer to add files explicitly.&lt;/p&gt;
&lt;h2&gt;Creating the commit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git commit -m &quot;Add User class&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works, but the message could be better.&lt;/p&gt;
&lt;p&gt;A good commit message answers two questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What changed?&lt;/li&gt;
&lt;li&gt;Why did it change?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Writing Good Commit Messages&lt;/h2&gt;
&lt;p&gt;A commonly used structure is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Short summary (max ~50 characters)

Detailed explanation of why the change was needed.
Explain important technical details if necessary.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Add User class for authentication module
Stores name and email fields used in login flow.
Email must be unique and will be validated by database
constraints in the next task.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good commit messages help future developers understand the reasoning behind code changes.&lt;/p&gt;
&lt;p&gt;And often that future developer is you six months later.&lt;/p&gt;
&lt;h2&gt;Conventional Commits&lt;/h2&gt;
&lt;p&gt;Many teams use the Conventional Commits standard.&lt;/p&gt;
&lt;p&gt;It adds a prefix describing the type of change.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feat: add password reset endpoint
fix: prevent crash on empty email
docs: update installation guide
refactor: move validation logic to service
test: add login unit tests
chore: update dependencies
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Common prefixes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefix&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;feat&lt;/td&gt;
&lt;td&gt;new feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fix&lt;/td&gt;
&lt;td&gt;bug fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;documentation changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;style&lt;/td&gt;
&lt;td&gt;formatting only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;refactor&lt;/td&gt;
&lt;td&gt;code improvements without behavior change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;test&lt;/td&gt;
&lt;td&gt;new or updated tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chore&lt;/td&gt;
&lt;td&gt;tooling or configuration changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Example commit history:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feat: add User entity
fix: validate email format
refactor: extract email validation utility
test: add negative login tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes the project history easy to understand.&lt;/p&gt;
&lt;p&gt;It also enables tools that automatically generate changelogs.&lt;/p&gt;
&lt;h2&gt;Sending Code to GitHub&lt;/h2&gt;
&lt;p&gt;Once your work is committed, push it to the remote repository.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin feature/add-user-model
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the branch doesn&apos;t exist on the server yet, Git will create it.&lt;/p&gt;
&lt;p&gt;After pushing, go to GitHub (or GitLab) and you&apos;ll see a button:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create Pull Request&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;What Is a Pull Request?&lt;/h2&gt;
&lt;p&gt;A Pull Request (PR) is a request to merge your changes into another branch.&lt;/p&gt;
&lt;p&gt;Usually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feature branch → main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pull requests allow teammates to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;review code&lt;/li&gt;
&lt;li&gt;suggest improvements&lt;/li&gt;
&lt;li&gt;detect bugs early&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This …</content:encoded></item><item><title>Meet Effect TS: A New Way to Structure TypeScript Apps</title><link>https://jsdev.space/meet-effect-ts/</link><guid isPermaLink="true">https://jsdev.space/meet-effect-ts/</guid><description>Discover Effect TS, a powerful TypeScript framework for typed errors, dependency injection, structured concurrency, and composable backend architecture.</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The JavaScript ecosystem evolves extremely fast. New frameworks appear
constantly, each promising better performance, better developer
experience, or a simpler programming model.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://effect.website/&quot;&gt;Effect TS&lt;/a&gt; is different.&lt;/p&gt;
&lt;p&gt;It is not primarily a UI framework. It is not a backend framework in the
traditional sense either. Instead, Effect is an attempt to solve
something deeper: &lt;strong&gt;how complex TypeScript applications should be
structured&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Most real-world applications suffer from the same problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;asynchronous code becomes messy&lt;/li&gt;
&lt;li&gt;errors are unpredictable&lt;/li&gt;
&lt;li&gt;dependency injection becomes fragile&lt;/li&gt;
&lt;li&gt;background tasks are hard to manage&lt;/li&gt;
&lt;li&gt;libraries do not compose well together&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Effect TS approaches these problems from a new angle. Instead of solving
them separately, it introduces a unified programming model where
&lt;strong&gt;errors, dependencies, concurrency, and resources are all part of the
type system&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At first glance this model may look unusual. But once developers start
building real systems with it, many realize that it solves issues they
previously handled with dozens of separate libraries.&lt;/p&gt;
&lt;p&gt;This article introduces the core ideas behind Effect and explains why
more developers are experimenting with it in their TypeScript projects.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Developers Are Trying Effect&lt;/h2&gt;
&lt;p&gt;Effect has been gaining attention because it combines several powerful
ideas into a single framework.&lt;/p&gt;
&lt;p&gt;Below are ten practical reasons why developers start exploring it.&lt;/p&gt;
&lt;h2&gt;1. Errors Become Part of the Type System&lt;/h2&gt;
&lt;p&gt;Traditional JavaScript error handling is chaotic. A function can throw
anything: an Error instance, a string, or even an arbitrary object.&lt;/p&gt;
&lt;p&gt;In many projects this leads to code where the real failure scenarios are
not clearly defined.&lt;/p&gt;
&lt;p&gt;Effect changes this by making errors explicit.&lt;/p&gt;
&lt;p&gt;Instead of writing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function getUser(id: string): Promise&amp;lt;User&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you describe both success and failure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Effect&amp;lt;User, UserNotFoundError | NetworkError&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the compiler knows exactly which errors are possible. When those
errors are handled later, the type system verifies that no cases were
forgotten.&lt;/p&gt;
&lt;p&gt;This makes applications far more predictable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Dependency Injection Without Magic&lt;/h2&gt;
&lt;p&gt;Many backend frameworks use dependency injection containers based on
decorators or runtime reflection.&lt;/p&gt;
&lt;p&gt;While these systems work, they also hide dependencies behind runtime
behavior.&lt;/p&gt;
&lt;p&gt;Effect takes a different approach.&lt;/p&gt;
&lt;p&gt;Dependencies are represented directly in types. If a function requires a
service, the type signature reflects that requirement. The compiler
ensures the dependency is provided before the program runs.&lt;/p&gt;
&lt;p&gt;This eliminates an entire category of runtime errors.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Testing Becomes Much Simpler&lt;/h2&gt;
&lt;p&gt;Because dependencies are explicit, testing becomes easier.&lt;/p&gt;
&lt;p&gt;Instead of mocking modules globally or configuring complicated
containers, you can simply replace a service implementation with a test
version.&lt;/p&gt;
&lt;p&gt;This leads to tests that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;easier to understand&lt;/li&gt;
&lt;li&gt;easier to maintain&lt;/li&gt;
&lt;li&gt;less dependent on internal implementation details&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For large systems this improvement alone can be extremely valuable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Everything Is Designed to Compose&lt;/h2&gt;
&lt;p&gt;Effect encourages a compositional style of programming.&lt;/p&gt;
&lt;p&gt;Instead of deeply nested async code, logic is built as small pieces that
combine through pipelines.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pipe(
  fetchUsers(),
  Effect.map(filterActiveUsers),
  Effect.tap(logUserCount),
  Effect.flatMap(saveUsers)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each step transforms the previous result.&lt;/p&gt;
&lt;p&gt;This approach keeps complex workflows readable and easy to refactor.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Gradual Adoption Is Possible&lt;/h2&gt;
&lt;p&gt;One of the biggest advantages of Effect is that it does not require
rewriting an entire application.&lt;/p&gt;
&lt;p&gt;Existing Promise-based code can be wrapped using helper utilities.
Effects can also be executed as regular Promises.&lt;/p&gt;
&lt;p&gt;This means teams can adopt Effect incrementally. A project might start
with a few critical services and expand from there.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Concurrency Is Built Into the Model&lt;/h2&gt;
&lt;p&gt;JavaScript applications often require complex asynchronous workflows.&lt;/p&gt;
&lt;p&gt;Typical solutions involve combining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Promise.all&lt;/li&gt;
&lt;li&gt;retry libraries&lt;/li&gt;
&lt;li&gt;AbortController&lt;/li&gt;
&lt;li&gt;custom scheduling logic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Effect replaces this with a unified concurrency model.&lt;/p&gt;
&lt;p&gt;The framework provides tools for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;parallel execution&lt;/li&gt;
&lt;li&gt;task cancellation&lt;/li&gt;
&lt;li&gt;retries with backoff&lt;/li&gt;
&lt;li&gt;scheduling&lt;/li&gt;
&lt;li&gt;background tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All using the same abstractions.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. A Rich Built-In Toolkit&lt;/h2&gt;
&lt;p&gt;Effect is not just a runtime. It also includes a large ecosystem of
utilities.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Streams&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;PubSub channels&lt;/li&gt;
&lt;li&gt;Schedulers&lt;/li&gt;
&lt;li&gt;Date/time helpers&lt;/li&gt;
&lt;li&gt;transactional memory&lt;/li&gt;
&lt;li&gt;schema validation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of installing many unrelated libraries, developers often rely on
the tools provided by Effect itself.&lt;/p&gt;
&lt;p&gt;This reduces fragmentation in large codebases.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Observability Is Built In&lt;/h2&gt;
&lt;p&gt;Modern applications require visibility into their behavior.&lt;/p&gt;
&lt;p&gt;Logging, metrics, and tracing are essential for debugging and monitoring
production systems.&lt;/p&gt;
&lt;p&gt;Effect integrates observability directly into the framework and supports
standards such as OpenTelemetry.&lt;/p&gt;
&lt;p&gt;This makes it easier to instrument applications without complex
integrations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. A Growing Ecosystem&lt;/h2&gt;
&lt;p&gt;The core Effect team maintains several official packages that extend the
framework.&lt;/p&gt;
&lt;p&gt;Some examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;@effect/platform&lt;/strong&gt; -- platform utilities like HTTP and filesystem&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/sql&lt;/strong&gt; -- typed database access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/cli&lt;/strong&gt; -- command‑line application framework&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/rpc&lt;/strong&gt; -- typed remote procedure calls&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/cluster&lt;/strong&gt; -- primitives for distributed systems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/ai&lt;/strong&gt; -- abstractions for language model integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because these packages follow the same design philosophy, they integrate
smoothly with each other.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Strong Type Feedback Helps AI Tools&lt;/h2&gt;
&lt;p&gt;AI‑assisted development tools rely heavily on compiler feedback.&lt;/p&gt;
&lt;p&gt;Effect&apos;s strong typing provides extremely clear signals when generated
code is incorrect.&lt;/p&gt;
&lt;p&gt;This often leads to faster corrections and fewer runtime bugs when using
AI coding assistants.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Core Idea: Effects&lt;/h2&gt;
&lt;p&gt;At the heart of the framework lies a single abstraction:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Effect&amp;lt;A, E, R&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This type represents a computation.&lt;/p&gt;
&lt;p&gt;The three parameters describe:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt; --- the successful result&lt;br /&gt;
&lt;strong&gt;E&lt;/strong&gt; --- the error type&lt;br /&gt;
&lt;strong&gt;R&lt;/strong&gt; --- required dependencies&lt;/p&gt;
&lt;p&gt;Compared to a Promise, which only represents success, Effect provides a
complete description of a computation.&lt;/p&gt;
&lt;p&gt;This allows the compiler to reason about behavior that would otherwise
only appear at runtime.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Creating Effects&lt;/h2&gt;
&lt;p&gt;The framework provides several helpers for constructing effects.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const value = Effect.succeed(42)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creating a failure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const error = Effect.fail(&quot;Unexpected error&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wrapping synchronous logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const random = Effect.sync(() =&amp;gt; Math.random())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These operations describe work rather than executing it immediatel…</content:encoded></item><item><title>Howto Deploy OpenClaw and Build Your Personal AI Second Brain</title><link>https://jsdev.space/howto/deploy-openclaw-second-brain/</link><guid isPermaLink="true">https://jsdev.space/howto/deploy-openclaw-second-brain/</guid><description>Learn how to deploy OpenClaw from scratch on Windows, macOS, or Linux and build a private AI assistant with memory, tool integrations, and automation.</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most AI assistants today are little more than chat interfaces. They
answer questions but cannot access your tools, remember your
preferences, or perform real work. &lt;a href=&quot;https://openclaw.ai/&quot;&gt;OpenClaw&lt;/a&gt; takes a different approach.&lt;/p&gt;
&lt;p&gt;Instead of functioning purely as a chatbot, OpenClaw acts as a
&lt;strong&gt;programmable cognitive partner&lt;/strong&gt;. It can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remember long‑term context&lt;/li&gt;
&lt;li&gt;connect to external tools&lt;/li&gt;
&lt;li&gt;automate workflows&lt;/li&gt;
&lt;li&gt;run entirely on infrastructure you control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide explains how to deploy OpenClaw from scratch and configure it
on &lt;strong&gt;Windows, macOS, or Linux&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;By the end of this tutorial you will have a working OpenClaw instance
running locally.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Use OpenClaw&lt;/h2&gt;
&lt;h3&gt;Private Deployment&lt;/h3&gt;
&lt;p&gt;All conversations and memory stay on your own machine or server.&lt;/p&gt;
&lt;h3&gt;Tool Integration&lt;/h3&gt;
&lt;p&gt;OpenClaw can interact with tools such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;calendars&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;automation workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Persistent Memory&lt;/h3&gt;
&lt;p&gt;OpenClaw includes both &lt;strong&gt;short‑term and long‑term memory&lt;/strong&gt;, allowing it
to remember:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user preferences&lt;/li&gt;
&lt;li&gt;project details&lt;/li&gt;
&lt;li&gt;conversation context&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Open Source&lt;/h3&gt;
&lt;p&gt;OpenClaw is released under the MIT license, meaning the entire system is
fully auditable and customizable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;System Requirements&lt;/h2&gt;
&lt;p&gt;Minimum environment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Node.js 18+
npm or pnpm
8 GB RAM recommended
2 GB disk space
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Supported platforms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;li&gt;Windows (PowerShell or WSL2)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Verify Your Environment&lt;/h2&gt;
&lt;h3&gt;macOS / Linux&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;node --version
npm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If Node is missing, install it from:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/&quot;&gt;Node.js&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Installation Method 1: One‑Step Installer&lt;/h2&gt;
&lt;h3&gt;macOS / Linux&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;curl -fsSL https://docs.openclaw.ai/install.sh | bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After installation open:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost:18789&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Windows (PowerShell)&lt;/h3&gt;
&lt;p&gt;Clone the repository and start the gateway manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/openclawai/openclaw.git
cd openclaw
npm install
npm run start:gateway
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Windows with WSL2 (Recommended)&lt;/h3&gt;
&lt;p&gt;Install WSL:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wsl --install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then inside WSL:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -fsSL https://docs.openclaw.ai/install.sh | bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Access the interface via:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost:18789&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Installation Method 2: Manual Installation&lt;/h2&gt;
&lt;h3&gt;Clone Repository&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/openclawai/openclaw.git
cd openclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Install Dependencies&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install -g pnpm
pnpm install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Configure Workspace&lt;/h3&gt;
&lt;p&gt;macOS / Linux&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export OPENCLAW_WORKSPACE=~/.openclaw/workspace
mkdir -p $OPENCLAW_WORKSPACE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows PowerShell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setx OPENCLAW_WORKSPACE &quot;%USERPROFILE%\.openclaw\workspace&quot;
mkdir %USERPROFILE%\.openclaw\workspace
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Copy Configuration&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cp config.example.yaml config.yaml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;p&gt;&lt;code&gt;copy config.example.yaml config.yaml&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Start Gateway&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pnpm start:gateway&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker Deployment&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    version: &quot;3.8&quot;

    services:
      openclaw:
        image: ghcr.io/openclaw/openclaw:latest
        container_name: openclaw
        ports:
          - &quot;18789:18789&quot;
          - &quot;18792:18792&quot;
        environment:
          - OPENCLAW_WORKSPACE=/workspace
          - NODE_ENV=production
        volumes:
          - ./workspace:/workspace
          - ./config.yaml:/app/config.yaml
          - ./logs:/var/log/openclaw
        restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the container:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;View logs:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose logs -f&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Configuring AI Models&lt;/h2&gt;
&lt;p&gt;Example configuration in &lt;code&gt;config.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    agents:
      defaults:
        model: &quot;siliconflow/deepseek-ai/DeepSeek-V3.2&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternative models:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openai/gpt-4
anthropic/claude-3-opus
google/gemini-2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Enabling Skills&lt;/h2&gt;
&lt;p&gt;Skills allow OpenClaw to interact with external services.&lt;/p&gt;
&lt;p&gt;Example configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    skills:
      enabled:
        - github
        - weather
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install additional skills:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;openclaw skills install github&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Creating Your First Custom Skill&lt;/h3&gt;
&lt;p&gt;Create directory:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir ~/.openclaw/workspace/skills/my-skill&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;SKILL.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## Custom Skill

### Description
Automation skill for internal APIs.

### Usage
/my-skill [arguments]

### Capabilities
- API access
- file reading
- workflow automation
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;h3&gt;Port Already In Use&lt;/h3&gt;
&lt;p&gt;macOS / Linux&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lsof -i :18789&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;p&gt;&lt;code&gt;netstat -ano | findstr 18789&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Kill process:&lt;/p&gt;
&lt;p&gt;Linux/macOS&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkill -f openclaw&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;p&gt;&lt;code&gt;taskkill /PID &amp;lt;pid&amp;gt; /F&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;OpenClaw is more than a chatbot. It is a programmable AI platform
capable of integrating with real workflows, remembering context, and
evolving through custom skills.&lt;/p&gt;
&lt;p&gt;Once deployed, it can become a powerful &lt;strong&gt;personal AI infrastructure
layer&lt;/strong&gt; that helps automate tasks, analyze code, and manage complex
workflows.&lt;/p&gt;
&lt;p&gt;For developers interested in building their own AI ecosystem, OpenClaw
provides a strong foundation for creating a true &lt;strong&gt;digital second
brain&lt;/strong&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Friday Links #36 — JavaScript Ecosystem Weekly</title><link>https://jsdev.space/friday/friday-36/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-36/</guid><description>A curated roundup of JavaScript ecosystem news: TypeScript, Node.js, Deno, AI developer tools, security discoveries, and new libraries from the past two weeks.</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-36.png&quot; alt=&quot;Friday Links #36&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The JavaScript ecosystem never slows down.&lt;br /&gt;
Over the past two weeks we’ve seen &lt;strong&gt;major updates across runtimes, frameworks, tooling, and security&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this issue of &lt;strong&gt;Friday Links&lt;/strong&gt;, we highlight the most interesting developments across the JavaScript world — from infrastructure changes and new tools to security research and ecosystem trends.&lt;/p&gt;
&lt;h2&gt;🧠 Ecosystem Highlights&lt;/h2&gt;
&lt;h3&gt;TypeScript 6 Prepares the Path to TS7&lt;/h3&gt;
&lt;p&gt;The TypeScript team &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-rc/&quot;&gt;released&lt;/a&gt; an early preview of &lt;strong&gt;TypeScript 6&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This release is mainly about &lt;strong&gt;internal changes preparing for the future Go-based compiler planned for TypeScript 7&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Key goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;faster compilation&lt;/li&gt;
&lt;li&gt;reduced memory usage&lt;/li&gt;
&lt;li&gt;better incremental builds&lt;/li&gt;
&lt;li&gt;improved large project performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Large monorepos could see &lt;strong&gt;dramatic speed improvements&lt;/strong&gt; once the Go compiler lands.&lt;/p&gt;
&lt;h3&gt;Deno 2.7 Improves Node Compatibility&lt;/h3&gt;
&lt;p&gt;The latest &lt;a href=&quot;https://deno.com/blog/v2.7&quot;&gt;&lt;strong&gt;Deno runtime release&lt;/strong&gt;&lt;/a&gt; continues improving Node compatibility.&lt;/p&gt;
&lt;p&gt;Highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;improved npm integration&lt;/li&gt;
&lt;li&gt;Node API compatibility&lt;/li&gt;
&lt;li&gt;Temporal API stabilization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const now = Temporal.Now.instant()
console.log(now.toString())
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.blog/ai-and-ml/generative-ai/under-the-hood-security-architecture-of-github-agentic-workflows/&quot;&gt;Under the hood: Security architecture of GitHub Agentic Workflows&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/beating-javascript-performance-limits-with-rust-and-n-api-building-a-faster-image-diff-tool&quot;&gt;Beating JavaScript Performance Limits With Rust and N-API: Building a Faster Image Diff Tool&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/the-different-ways-to-select-html-in-css/&quot;&gt;The Different Ways to Select &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; in CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/the-big-gotcha-of-anchor-positioning/&quot;&gt;The Big Gotcha of Anchor Positioning&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://neciudan.dev/cline-ci-got-compromised-here-is-how&quot;&gt;How to steal npm publish tokens by opening GitHub issues&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cardog.app/blog/vin-decoder-javascript&quot;&gt;How to Decode a VIN in JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.scottlogic.com/2026/03/09/noJS-3-flappy-bird.html&quot;&gt;Making a Flappy Bird clone using pure HTML and CSS, no JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ishchhabra.com/writing/pnpm-monorepo&quot;&gt;How to build a pnpm monorepo, the right way&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mux.com/blog/react-is-changing-the-game-for-streaming-apps-with-the-activity-component&quot;&gt;React is changing the game for streaming apps with the Activity component&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2026-03-09-using-css-animations-as-state-machines-to-remember-focus-and-hover-states-with-css-only/&quot;&gt;Using CSS animations as state machines to remember focus and hover states with CSS only&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.frankmtaylor.com/2026/03/05/you-dont-know-html-tables/&quot;&gt;You Don’t Know HTML Tables&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://reactdevelopment.substack.com/p/5-react-hooks-techniques-to-improve&quot;&gt;5 React Hooks Techniques to Improve Component Performance&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;h3&gt;Repomix — Turn Any Repo Into a Single AI-Readable File&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/repomix.png&quot; alt=&quot;Repomix&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadashy/repomix&quot;&gt;Repomix&lt;/a&gt; packs an entire repository into a single AI-friendly document.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tb5z035i/cursor-tg&quot;&gt;Cursor Cloud Telegram Connector&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://npmx.dev/&quot;&gt;npmx&lt;/a&gt; is an experimental tool designed to improve npm package exploration.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://litepacks.github.io/welyjs/&quot;&gt;Wely&lt;/a&gt; — Lightweight Web Component Framework&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vadimdemedes/ink&quot;&gt;Ink&lt;/a&gt; allows developers to build CLI tools using React components.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sojinantony01.github.io/react-cron-generator/&quot;&gt;Cron Expression Generator&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel/nft&quot;&gt;Node File Trace&lt;/a&gt; - determines exactly which files a Node application needs to run.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/privatenumber/minification-benchmarks&quot;&gt;JavaScript Minification Benchmarks&lt;/a&gt;: SWC Still Leads&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rv-grid.com/&quot;&gt;RevoGrid&lt;/a&gt; - High-Performance Data Grid Component&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/cosmiciron/vmprint&quot;&gt;VMPrint&lt;/a&gt; - A pure-JS, tiny typesetting engine with bit-perfect PDF output on everything—from Cloudflare Workers to the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/quantizor/markdown-to-jsx&quot;&gt;markdown-to-jsx&lt;/a&gt; -  A very fast and versatile markdown toolchain. Output to AST, React, React Native, SolidJS, Vue, HTML, and more!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sindresorhus/clipboardy&quot;&gt;clipboardy&lt;/a&gt; -  Access the system clipboard (copy/paste)&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/solidjs/solid/releases/tag/v2.0.0-beta.0&quot;&gt;Solid v2.0.0 Beta: The &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; Era Comes to an End&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After a long experimental phase, Solid 2.0 has released its first beta, introducing native asynchronous reactivity as a core feature of the framework.&lt;/p&gt;
&lt;p&gt;In this new model, reactive computations can directly return Promises or async iterables, and Solid’s reactive graph will automatically suspend and resume around those async operations. This removes much of the complexity developers previously had to manage when dealing with asynchronous state.&lt;/p&gt;
&lt;p&gt;One notable change is that &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; has been retired. For initial renders, it is now replaced by a simpler component called &lt;code&gt;&amp;lt;Loading&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build/blog/astro-6/&quot;&gt;Astro 6 is here!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/en/blog/release/v25.8.0&quot;&gt;Node.js 25.8.0 (Current)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://eslint.org/blog/2026/03/eslint-v10.0.3-released/&quot;&gt;ESLint v10.0.3 released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.emberjs.com/ember-released-6-11/&quot;&gt;Ember 6.11 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ionic.io/blog/announcing-ionic-framework-8-8&quot;&gt;Ionic Framework 8.8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/facebook/react-native/releases/tag/v0.85.0-rc.0&quot;&gt;React Native 0.85 RC.0&lt;/a&gt;, &lt;a href=&quot;https://github.com/pnpm/pnpm/releases/tag/v10.32.0&quot;&gt;pnpm 10.32&lt;/a&gt;, &lt;a href=&quot;https://github.com/jestjs/jest/releases/tag/v30.3.0&quot;&gt;Jest 30.3&lt;/a&gt;, &lt;a href=&quot;https://github.com/recharts/recharts/releases/tag/v3.8.0&quot;&gt;Recharts 3.8&lt;/a&gt;,
&lt;a href=&quot;https://github.com/openplayerjs/openplayerjs/releases/tag/v3.0.2&quot;&gt;OpenPlayer.js 3.0.2&lt;/a&gt;, &lt;a href=&quot;https://github.com/prisma/prisma/releases/tag/7.5.0&quot;&gt;Prisma 7.5&lt;/a&gt;, &lt;a href=&quot;https://github.com/sqliteai/sqlite-js&quot;&gt;SQLite JS 1.3&lt;/a&gt;, &lt;a href=&quot;https://github.com/staylor/react-helmet-async/pull/260&quot;&gt;React Helmet Async 3.0&lt;/a&gt;, &lt;a href=&quot;https://github.com/preactjs/preact/releases/tag/10.29.0&quot;&gt;Preact 10.29.0&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📺 Videos&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=IBTx5aGj-6U&quot;&gt;Build Your Own Video Sharing App – Loom Clone with Next.js and Mux JavaScript Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_TRV6fPUMJw&quot;&gt;You Can Just Ship Agents: Architecting for the Agentic Era | Dom Sipowicz, Vercel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=wvt5JNUXXLM&quot;&gt;The Future of TypeScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=IBTx5aGj-6U&quot;&gt;Build Your Own Video Sharing App – Loom Clone with Next.js and Mux JavaScript Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=abbeIUOCzmw&quot;&gt;Cloudflare just slop forked Next.js…&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Xn-gtHDsaPY&quot;&gt;7 new open source AI tools you need right now…&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.…</content:encoded></item><item><title>Valibot vs Zod: A Lightweight Validation Alternative</title><link>https://jsdev.space/valibot-vs-zod/</link><guid isPermaLink="true">https://jsdev.space/valibot-vs-zod/</guid><description>Compare Valibot and Zod in JavaScript and TypeScript apps. Explore bundle size, performance benchmarks, and real validation examples.</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Valibot vs Zod — Can a 1KB Validator Replace Zod?&lt;/h2&gt;
&lt;p&gt;Modern JavaScript applications rely heavily on external data sources. APIs, forms, query parameters, cookies, and configuration files all deliver data that your application must trust and process.&lt;/p&gt;
&lt;p&gt;But in practice, external data &lt;strong&gt;cannot be trusted&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Fields disappear. Types change. Backends evolve. A simple typo in a property name can break an entire page.&lt;/p&gt;
&lt;p&gt;Schema validation libraries solve this problem by adding a &lt;strong&gt;runtime validation layer&lt;/strong&gt; between your application and external data.&lt;/p&gt;
&lt;p&gt;Two libraries dominate this space today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zod.dev/&quot;&gt;Zod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://valibot.dev/&quot;&gt;Valibot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zod has been the standard for years. Valibot, however, is a newer library designed with a different philosophy: &lt;strong&gt;smaller bundles and faster validation&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Validation Libraries Matter&lt;/h2&gt;
&lt;p&gt;TypeScript provides compile‑time guarantees, but it cannot guarantee runtime correctness.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const response = await fetch(&quot;/api/user/1&quot;)
const user = await response.json()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Runtime data might not match expectations.&lt;/p&gt;
&lt;p&gt;Validation libraries ensure data integrity before it reaches business logic.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Validating API Responses with Valibot&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const ProductSchema = v.object({
  id: v.number(),
  title: v.string(),
  price: v.number(),
  rating: v.number(),
  images: v.array(v.string())
})

type Product = v.InferOutput&amp;lt;typeof ProductSchema&amp;gt;

export async function loadProduct(id:number):Promise&amp;lt;Product&amp;gt;{

  const res = await fetch(`https://dummyjson.com/products/${id}`)
  const data = await res.json()

  try{
    return v.parse(ProductSchema,data)
  }
  catch(error){
    console.error(&quot;Invalid API response&quot;,error)
    throw new Error(&quot;Product validation failed&quot;)
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Validating Form Input&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const RegistrationSchema = v.object({

  username:v.pipe(
    v.string(),
    v.minLength(3)
  ),

  email:v.pipe(
    v.string(),
    v.email()
  ),

  age:v.pipe(
    v.string(),
    v.digits(),
    v.transform(Number)
  ),

  password:v.pipe(
    v.string(),
    v.minLength(6)
  )

})

type RegistrationData = v.InferOutput&amp;lt;typeof RegistrationSchema&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Cross‑Field Validation&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const RegistrationSchema = v.pipe(

  v.object({

    email:v.pipe(
      v.string(&quot;Email must be text&quot;),
      v.email(&quot;Enter a valid email&quot;)
    ),

    age:v.pipe(
      v.string(),
      v.digits(&quot;Age must contain digits&quot;),
      v.transform(Number),
      v.minValue(18,&quot;You must be at least 18&quot;)
    ),

    password:v.pipe(
      v.string(),
      v.minLength(6,&quot;Password must contain at least 6 characters&quot;)
    ),

    confirmPassword:v.string()

  }),

  v.forward(
    v.partialCheck(
      [[&quot;password&quot;],[&quot;confirmPassword&quot;]],
      ({password,confirmPassword}) =&amp;gt; password === confirmPassword,
      &quot;Passwords do not match&quot;
    ),
    [&quot;confirmPassword&quot;]
  )

)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Code Comparison&lt;/h2&gt;
&lt;h3&gt;Valibot&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const BookSchema = v.object({
  title:v.string(),
  price:v.number()
})

v.parse(BookSchema,{title:&quot;JavaScript Guide&quot;,price:30})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Zod&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import {z} from &quot;zod&quot;

const BookSchema = z.object({
  title:z.string(),
  price:z.number()
})

BookSchema.parse({title:&quot;JavaScript Guide&quot;,price:30})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Benchmark Scripts&lt;/h2&gt;
&lt;h3&gt;Valibot&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const schema = v.object({
 id:v.number(),
 name:v.string(),
 price:v.number(),
 rating:v.number(),
 tags:v.array(v.string())
})

const sample = {
 id:1,
 name:&quot;Laptop&quot;,
 price:1500,
 rating:4.8,
 tags:[&quot;tech&quot;,&quot;computer&quot;]
}

function benchmarkValibot(iterations:number){

 const start = performance.now()

 for(let i=0;i&amp;lt;iterations;i++){
  v.parse(schema,sample)
 }

 return performance.now() - start

}

console.log(&quot;Valibot:&quot;,benchmarkValibot(10000),&quot;ms&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Zod&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import {z} from &quot;zod&quot;

const schema = z.object({
 id:z.number(),
 name:z.string(),
 price:z.number(),
 rating:z.number(),
 tags:z.array(z.string())
})

const sample = {
 id:1,
 name:&quot;Laptop&quot;,
 price:1500,
 rating:4.8,
 tags:[&quot;tech&quot;,&quot;computer&quot;]
}

function benchmarkZod(iterations:number){

 const start = performance.now()

 for(let i=0;i&amp;lt;iterations;i++){
  schema.parse(sample)
 }

 return performance.now() - start

}

console.log(&quot;Zod:&quot;,benchmarkZod(10000),&quot;ms&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Benchmark Results&lt;/h2&gt;
&lt;h3&gt;Valid data&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;min (ms)&lt;/th&gt;
&lt;th&gt;max (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valibot&lt;/td&gt;
&lt;td&gt;26.58&lt;/td&gt;
&lt;td&gt;27.31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zod&lt;/td&gt;
&lt;td&gt;67.02&lt;/td&gt;
&lt;td&gt;70.25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Invalid data&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;min (ms)&lt;/th&gt;
&lt;th&gt;max (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valibot&lt;/td&gt;
&lt;td&gt;48.63&lt;/td&gt;
&lt;td&gt;54.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zod&lt;/td&gt;
&lt;td&gt;143.63&lt;/td&gt;
&lt;td&gt;147.79&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Bundle Size&lt;/h2&gt;
&lt;p&gt;Replacing Zod with Valibot reduced bundle size by &lt;strong&gt;~11.4 KB (gzip)&lt;/strong&gt; in a medium project.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Popularity&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Weekly downloads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valibot&lt;/td&gt;
&lt;td&gt;~1.2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zod&lt;/td&gt;
&lt;td&gt;~89.5M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Validation libraries protect applications from unreliable external data.&lt;/p&gt;
&lt;p&gt;Valibot proves that validation can be &lt;strong&gt;fast, lightweight, and developer‑friendly&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For new projects, Valibot is definitely worth considering.&lt;/p&gt;
</content:encoded></item><item><title>Why Blindly Using JSON.parse() Can Be Dangerous</title><link>https://jsdev.space/safe-json-parse-javascript/</link><guid isPermaLink="true">https://jsdev.space/safe-json-parse-javascript/</guid><description>Learn why blindly using JSON.parse() can introduce security risks like prototype pollution and DoS attacks, and how to safely parse JSON in modern JavaScript.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you write JavaScript for long enough—whether in the browser or on a Node.js server—you’ll almost certainly use &lt;code&gt;JSON.parse()&lt;/code&gt; countless times.&lt;/p&gt;
&lt;p&gt;It’s one of the most common APIs in the ecosystem.&lt;/p&gt;
&lt;p&gt;Developers rely on it for everything from reading configuration files to handling API requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const user = JSON.parse(localStorage.getItem(&quot;user&quot;))

const payload = JSON.parse(req.body.payload)

const config = JSON.parse(fs.readFileSync(&quot;config.json&quot;, &quot;utf-8&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s simple, fast, and built directly into the language.&lt;/p&gt;
&lt;p&gt;But here’s the problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many developers treat &lt;code&gt;JSON.parse()&lt;/code&gt; as a harmless utility, when in reality it can become a security risk if used carelessly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The function itself isn’t dangerous—but blindly parsing untrusted input can expose your application to attacks such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prototype pollution&lt;/li&gt;
&lt;li&gt;Denial of Service (DoS)&lt;/li&gt;
&lt;li&gt;Data injection attacks&lt;/li&gt;
&lt;li&gt;Unexpected runtime crashes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, we’ll explore why this happens and &lt;strong&gt;how to implement secure JSON parsing strategies in modern JavaScript applications&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Understanding What JSON.parse() Actually Does&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; converts a JSON string into a JavaScript object.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const json = &apos;{&quot;name&quot;: &quot;Alice&quot;, &quot;role&quot;: &quot;admin&quot;}&apos;

const user = JSON.parse(json)

console.log(user.name)
// Alice
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function faithfully reconstructs the &lt;strong&gt;entire object structure&lt;/strong&gt; contained in the JSON string.&lt;/p&gt;
&lt;p&gt;That includes every property name provided by the input.&lt;/p&gt;
&lt;p&gt;And that’s where the problems start.&lt;/p&gt;
&lt;h2&gt;Security Risk #1: Prototype Pollution&lt;/h2&gt;
&lt;p&gt;One of the most common vulnerabilities related to unsafe JSON handling is prototype pollution.&lt;/p&gt;
&lt;p&gt;Prototype pollution occurs when attackers manipulate an object&apos;s prototype to inject properties into every object in the application.&lt;/p&gt;
&lt;p&gt;If your code merges or copies parsed objects without validation, an attacker can inject special keys like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__proto__
constructor
prototype
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These keys can modify global object behavior.&lt;/p&gt;
&lt;h3&gt;Example of a Prototype Pollution Payload&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const payload = &apos;{&quot;__proto__&quot;: {&quot;isAdmin&quot;: true}}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine parsing it and merging the object into your system:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = JSON.parse(payload)

Object.assign({}, data)

console.log({}.isAdmin)
// true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suddenly every object in your application has a new property.&lt;/p&gt;
&lt;p&gt;This can lead to severe consequences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication bypass&lt;/li&gt;
&lt;li&gt;Privilege escalation&lt;/li&gt;
&lt;li&gt;Corrupted application logic&lt;/li&gt;
&lt;li&gt;Security policy violations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many well-known vulnerabilities in JavaScript ecosystems have involved prototype pollution through unvalidated input.&lt;/p&gt;
&lt;h3&gt;Why This Happens&lt;/h3&gt;
&lt;p&gt;JavaScript objects inherit from &lt;code&gt;Object.prototype&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If attackers manage to inject properties into the prototype chain, the impact becomes global.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log({}.toString)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works because every object inherits from &lt;code&gt;Object.prototype&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If attackers add properties there, every object in your system changes behavior.&lt;/p&gt;
&lt;h2&gt;Security Risk #2: Denial of Service (DoS)&lt;/h2&gt;
&lt;p&gt;Another common attack vector is resource exhaustion.&lt;/p&gt;
&lt;p&gt;Attackers can send extremely large or deeply nested JSON strings that consume huge amounts of CPU or memory during parsing.&lt;/p&gt;
&lt;h3&gt;Example: Deep Nesting Attack&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const evil = &apos;{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{}}}}}}}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even small nested objects can cause heavy recursion.&lt;/p&gt;
&lt;p&gt;With enough depth, parsing can freeze the event loop.&lt;/p&gt;
&lt;h3&gt;Example: Huge Array Attack&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const payload = `[${&quot;1,&quot;.repeat(10000000)}1]`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A JSON string representing ten million elements can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allocate gigabytes of memory&lt;/li&gt;
&lt;li&gt;Freeze Node.js for seconds&lt;/li&gt;
&lt;li&gt;Crash the process with an Out Of Memory error&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For public APIs, this effectively becomes a remote kill switch.&lt;/p&gt;
&lt;h2&gt;Secure JSON Parsing Strategy&lt;/h2&gt;
&lt;p&gt;The safest approach combines three defensive layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Input size limits&lt;/li&gt;
&lt;li&gt;Key filtering&lt;/li&gt;
&lt;li&gt;Schema validation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s look at each one.&lt;/p&gt;
&lt;h3&gt;Step 1: Limit Input Size&lt;/h3&gt;
&lt;p&gt;Before parsing JSON, always check the size of the input.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function safeParse(jsonString, maxSize = 100 * 1024) {
  if (typeof jsonString !== &quot;string&quot;) {
    throw new Error(&quot;Invalid input type&quot;)
  }

  if (jsonString.length &amp;gt; maxSize) {
    throw new Error(&quot;JSON payload too large&quot;)
  }

  return JSON.parse(jsonString)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents attackers from sending extremely large payloads.&lt;/p&gt;
&lt;p&gt;In production APIs, limits are often set between &lt;strong&gt;50KB and 1MB&lt;/strong&gt;, depending on use case.&lt;/p&gt;
&lt;h3&gt;Step 2: Block Dangerous Keys&lt;/h3&gt;
&lt;p&gt;JavaScript allows a reviver function when parsing JSON.&lt;/p&gt;
&lt;p&gt;This lets you inspect every property during parsing.&lt;/p&gt;
&lt;p&gt;You can use it to reject suspicious keys.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function secureParse(jsonString) {
  return JSON.parse(jsonString, (key, value) =&amp;gt; {
    if (key === &quot;__proto__&quot; || key === &quot;constructor&quot; || key === &quot;prototype&quot;) {
      throw new Error(&quot;Forbidden key detected&quot;)
    }

    return value
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple check prevents prototype pollution payloads.&lt;/p&gt;
&lt;h3&gt;Step 3 (Best Practice): Runtime Schema Validation&lt;/h3&gt;
&lt;p&gt;Modern JavaScript applications rarely trust raw data.&lt;/p&gt;
&lt;p&gt;Instead, they validate data structures using schema validation libraries like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zod&lt;/li&gt;
&lt;li&gt;Joi&lt;/li&gt;
&lt;li&gt;Yup&lt;/li&gt;
&lt;li&gt;Ajv&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These tools ensure the parsed data matches an expected structure.&lt;/p&gt;
&lt;h3&gt;Example Using Zod&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  isAdmin: z.boolean().optional()
})

function parseUser(json) {
  const parsed = secureParse(json)

  return UserSchema.parse(parsed)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic runtime validation&lt;/li&gt;
&lt;li&gt;Clear error messages&lt;/li&gt;
&lt;li&gt;Type inference for TypeScript&lt;/li&gt;
&lt;li&gt;Protection against unexpected fields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Schema validation is considered modern best practice in production systems.&lt;/p&gt;
&lt;h2&gt;Additional Risks in Node.js&lt;/h2&gt;
&lt;p&gt;Server environments are especially vulnerable because JSON often comes from external sources.&lt;/p&gt;
&lt;p&gt;Common risky inputs include:&lt;/p&gt;
&lt;h3&gt;HTTP Requests&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;req.body
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Uploaded Configuration Files&lt;/h3&gt;
&lt;p&gt;Users may upload JSON configuration files.&lt;/p&gt;
&lt;p&gt;If parsed blindly, they can trigger crashes.&lt;/p&gt;
&lt;h3&gt;Database Data&lt;/h3&gt;
&lt;p&gt;Stored JSON may contain unexpected fields or corrupted structures.&lt;/p&gt;
&lt;h3&gt;Third-Party Webhooks&lt;/h3&gt;
&lt;p&gt;External services send JSON payloads that should never be trusted blindly.&lt;/p&gt;
&lt;h2&gt;Recommended Defensive Checklist&lt;/h2&gt;
&lt;p&gt;Before parsing JSON in production systems, verify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data source is trusted&lt;/li&gt;
&lt;li&gt;The payload size is limited&lt;/li&gt;
&lt;li&gt;Dangerous keys are filtered&lt;/li&gt;
&lt;li&gt;The structure matches a schema&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This layered approach dramatically reduces attack surfaces.&lt;/p&gt;
&lt;h2&gt;Is &lt;code&gt;JSON.parse()&lt;/code&gt; Actually Unsafe?&lt;/h2&gt;
&lt;p&gt;Not exactly.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; itself does not execute JavaScript code.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;eval()&lt;/code&gt;, it only reconstructs objects.&lt;/p&gt;
&lt;p&gt;However:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using it without validating input is equivalent to trusting user data blindly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And trusting user input is one of the most common sources of security vulnerabilities.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; is not a bad API.&lt;/p&gt;
&lt;p&gt;But using it without safeguards is like handling a powerful tool without protective equipm…</content:encoded></item><item><title>Tailwind CSS v4 vs MUI, Ant Design &amp; Styled Components</title><link>https://jsdev.space/tailwind-v4-vs-mui-antd-styled-components/</link><guid isPermaLink="true">https://jsdev.space/tailwind-v4-vs-mui-antd-styled-components/</guid><description>Architectural comparison of Tailwind CSS v4, MUI, Ant Design, and Styled Components—runtime costs, design tokens, dead CSS, theming, and scaling tradeoffs.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2026, picking a styling approach is less about taste and more about architecture.&lt;/p&gt;
&lt;p&gt;Teams don’t just “choose CSS.” They choose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how design decisions are encoded and shared,&lt;/li&gt;
&lt;li&gt;how fast UI can change without regressions,&lt;/li&gt;
&lt;li&gt;how much runtime work happens on every render,&lt;/li&gt;
&lt;li&gt;how quickly a codebase accumulates styling debt,&lt;/li&gt;
&lt;li&gt;and how portable the solution is across frameworks and products.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article compares four popular directions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS v4&lt;/a&gt; (utility-first styling engine)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mui.com/&quot;&gt;MUI&lt;/a&gt; and &lt;a href=&quot;https://ant.design/&quot;&gt;Ant Design&lt;/a&gt; (component libraries with strong opinions)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://styled-components.com/&quot;&gt;Styled Components&lt;/a&gt; (CSS-in-JS)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn’t to crown a universal winner. It’s to help you pick the right tool &lt;em&gt;for your system’s constraints&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;1. These tools are different “layers” of the stack&lt;/h2&gt;
&lt;p&gt;The biggest mistake is to compare them as if they are interchangeable.&lt;/p&gt;
&lt;h3&gt;MUI and Ant Design: component systems (not just styling)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/mui-ant-design.png&quot; alt=&quot;MUI and Ant Design&quot; /&gt;&lt;/p&gt;
&lt;p&gt;MUI and Ant Design are ready-made UI component ecosystems. You’re not just getting colors and spacing. You’re getting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;behavior,&lt;/li&gt;
&lt;li&gt;accessibility (A11Y) defaults,&lt;/li&gt;
&lt;li&gt;keyboard navigation,&lt;/li&gt;
&lt;li&gt;focus management in modals,&lt;/li&gt;
&lt;li&gt;edge-case handling that took years to harden.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need to ship an internal admin tool quickly, this matters.&lt;/p&gt;
&lt;p&gt;But you pay for it in other ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They impose a &lt;strong&gt;DOM structure&lt;/strong&gt; (wrappers, internal elements, slots).&lt;/li&gt;
&lt;li&gt;They impose an &lt;strong&gt;opinionated design language&lt;/strong&gt; (Material for MUI, Ant’s system for Ant).&lt;/li&gt;
&lt;li&gt;Customization often becomes an ongoing negotiation between your design system and theirs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your product design is “close enough” to their defaults, you move fast.
If your goal is “pixel-perfect Figma that looks like none of the defaults,” you can end up fighting the library.&lt;/p&gt;
&lt;h3&gt;Tailwind v4: styling engine (not a component library)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/tailwind.png&quot; alt=&quot;Tailwind v4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tailwind doesn’t know what a Date Picker is. It doesn’t ship one. It gives you low-level primitives to build your own.&lt;/p&gt;
&lt;p&gt;Tailwind’s advantage is control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you decide markup structure,&lt;/li&gt;
&lt;li&gt;you decide constraints,&lt;/li&gt;
&lt;li&gt;you can match a custom design system without constantly overriding component internals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tailwind v4 also reduced setup friction: you can start with a single CSS import (&lt;code&gt;@import &quot;tailwindcss&quot;;&lt;/code&gt;) instead of the older directive-heavy setup.&lt;/p&gt;
&lt;h3&gt;Styled Components: component-local styling with runtime generation&lt;/h3&gt;
&lt;p&gt;Styled Components sits between the two extremes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not a component library,&lt;/li&gt;
&lt;li&gt;but tightly binds CSS to components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is productive—until the project grows and you start asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which styles are still used?&lt;/li&gt;
&lt;li&gt;Which are dead?&lt;/li&gt;
&lt;li&gt;What’s the real cost of style generation during renders?&lt;/li&gt;
&lt;li&gt;How do we maintain consistent tokens without duplicating logic across many components?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tailwind and CSS-in-JS solve different problems. If you treat them as the same category, you’ll make architectural tradeoffs accidentall&lt;/p&gt;
&lt;h2&gt;2. Performance: static CSS vs runtime style generation&lt;/h2&gt;
&lt;p&gt;Performance isn’t just about “Tailwind is fast.” It’s about where work happens.&lt;/p&gt;
&lt;h3&gt;Runtime overhead (especially visible at scale)&lt;/h3&gt;
&lt;p&gt;CSS-in-JS solutions and some UI libraries generate and manage styles at runtime. That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extra JavaScript execution,&lt;/li&gt;
&lt;li&gt;extra style insertion/management,&lt;/li&gt;
&lt;li&gt;more work during render-heavy scenarios (large tables, infinite lists, fast state updates).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tailwind’s output is &lt;strong&gt;static CSS&lt;/strong&gt;. The browser receives it once and applies it like normal styles.&lt;/p&gt;
&lt;p&gt;That’s not automatically “always faster,” but it changes the performance profile in a way that becomes very noticeable in high-frequency UI updates.&lt;/p&gt;
&lt;h3&gt;Build performance in Tailwind v4&lt;/h3&gt;
&lt;p&gt;Tailwind v4 shipped with a “ground-up” performance rewrite and a faster build engine—reported as up to ~5× faster full builds and 100× faster incremental builds in the official announcements and coverage.&lt;/p&gt;
&lt;p&gt;That matters in real teams because build speed directly affects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iteration loops,&lt;/li&gt;
&lt;li&gt;design token tweaking,&lt;/li&gt;
&lt;li&gt;refactoring velocity,&lt;/li&gt;
&lt;li&gt;developer experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. “Self-cleaning CSS”: why dead styles don’t stick around (as much)&lt;/h2&gt;
&lt;p&gt;Large projects die by a thousand cuts—most of them are “small”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unused classes,&lt;/li&gt;
&lt;li&gt;abandoned component variants,&lt;/li&gt;
&lt;li&gt;theme overrides nobody remembers,&lt;/li&gt;
&lt;li&gt;CSS specificity battles that accumulate over years.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The typical CSS-in-JS failure mode&lt;/h3&gt;
&lt;p&gt;CSS-in-JS keeps styles “close” to components, which is great for local reasoning.&lt;/p&gt;
&lt;p&gt;But when components get removed or rewritten, styles can still remain in the bundle depending on usage patterns, exports, barrel files, and how build tooling marks code as reachable.&lt;/p&gt;
&lt;p&gt;Teams end up with a question they can’t easily answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Is this style still used anywhere?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Tailwind’s approach: generate only what’s referenced&lt;/h3&gt;
&lt;p&gt;Tailwind’s workflow is built around scanning your source for class usage and generating the necessary CSS for those classes. That “only what you used” approach is central to Tailwind’s output model.&lt;/p&gt;
&lt;p&gt;So when you delete a component, the classes that disappear from your markup will stop being included in the compiled CSS.&lt;/p&gt;
&lt;p&gt;This is one reason Tailwind projects often avoid the classic “CSS landfill” problem.&lt;/p&gt;
&lt;h3&gt;What about helpers like &lt;code&gt;tailwind-variants&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;Utilities like &lt;a href=&quot;https://www.tailwind-variants.org/&quot;&gt;&lt;code&gt;tailwind-variants&lt;/code&gt;&lt;/a&gt; (or any class-composition helper) don’t change the fundamental logic: Tailwind still cares about class strings it can discover. The important architectural point is that Tailwind’s output is driven by class usage, not “a stylesheet someone forgot to delete.”&lt;/p&gt;
&lt;h2&gt;4. Design tokens: from Figma to code without losing alignment&lt;/h2&gt;
&lt;p&gt;Modern UI teams don’t want “colors in CSS.” They want tokens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--color-brand&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--spacing-section&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--radius-card&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--font-sans&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tailwind v4’s CSS-first theming&lt;/h3&gt;
&lt;p&gt;Tailwind v4 introduced a CSS-first workflow where theme variables are defined using the @theme directive. Tailwind describes these as special CSS variables that influence which utilities exist.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@import &quot;tailwindcss&quot;;

@theme {
  --color-brand: oklch(0.55 0.22 260);
  --color-brand-hover: oklch(0.45 0.22 260);
  --font-sans: &quot;Inter&quot;, system-ui;
  --spacing-section: 4rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once defined, Tailwind can expose utilities based on those variables (for example, color-related tokens become usable via utility classes). The docs describe how theme variables map into usable styling in your project.&lt;/p&gt;
&lt;p&gt;Then UI usage becomes simple and consistent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function BrandButton() {
  return (
    &amp;lt;button className=&quot;bg-brand hover:bg-brand-hover text-white px-4 py-2 rounded&quot;&amp;gt;
      Button
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This token-driven flow aligns well with Figma Variables / token pipelines because it creates a single “source of truth” layer that can be shared across apps.&lt;/p&gt;
&lt;h3&gt;How this compares to MUI / Styled Components theming&lt;/h3&gt;
&lt;p&gt;MUI theming is powerful, but it typically lives in JavaScript objects and flows through providers and library AP…</content:encoded></item><item><title>JavaScript Note: ToggleEvent.source and dialog.closedBy</title><link>https://jsdev.space/toggleevent-source-dialog-closedby/</link><guid isPermaLink="true">https://jsdev.space/toggleevent-source-dialog-closedby/</guid><description>Practical guide to ToggleEvent.source and dialog.closedBy for cleaner dialog and popover behavior in modern browsers.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The modern web platform continues evolving with small but powerful
improvements. Two recent additions --- &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent/source&quot;&gt;&lt;strong&gt;&lt;code&gt;ToggleEvent.source&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; and the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby&quot;&gt;&lt;strong&gt;&lt;code&gt;closedby&lt;/code&gt; attribute for &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; --- make working with dialogs
and popovers significantly easier.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleEvent.source&lt;/code&gt; allows developers to determine &lt;strong&gt;which element
triggered a popover or dialog visibility change&lt;/strong&gt;, while &lt;code&gt;closedby&lt;/code&gt;
allows you to &lt;strong&gt;declare how a dialog can be closed&lt;/strong&gt; without writing
extra JavaScript.&lt;/p&gt;
&lt;p&gt;These additions move the web platform further toward &lt;strong&gt;declarative UI
behavior&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;ToggleEvent.source&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;source&lt;/code&gt; property&lt;/strong&gt; of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent&quot;&gt;&lt;code&gt;ToggleEvent&lt;/code&gt;&lt;/a&gt; interface is a
&lt;strong&gt;read‑only reference to the element that triggered the toggle event&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In simple terms, it tells you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Which element opened or closed a popover or dialog.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The property returns an instance of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element&quot;&gt;&lt;strong&gt;&lt;code&gt;Element&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If the visibility change was triggered &lt;strong&gt;programmatically&lt;/strong&gt;, the value
will be &lt;strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Browser support&lt;/h3&gt;
&lt;p&gt;Most modern browsers &lt;a href=&quot;https://caniuse.com/wf-toggleevent-source&quot;&gt;already support&lt;/a&gt; the property. Safari currently
exposes it behind an experimental flag.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/ToggleEvent-source.png&quot; alt=&quot;Browser support for ToggleEvent.source&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Elements that can trigger popovers&lt;/h3&gt;
&lt;p&gt;A popover can be triggered by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button commandfor=&quot;...&quot;&amp;gt;&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#commandfor&quot;&gt;commandfor attribute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button popovertarget=&quot;...&quot;&amp;gt;&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#popovertarget&quot;&gt;popovertarget attribute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;input type=&quot;button&quot; popovertarget=&quot;...&quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Elements that can behave as popovers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Any element with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover&quot;&gt;&lt;code&gt;popover&lt;/code&gt;&lt;/a&gt; attribute&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Consider a dialog with multiple buttons that close it.&lt;/p&gt;
&lt;p&gt;We want to determine &lt;strong&gt;which button closed the dialog&lt;/strong&gt; and display the
result.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;button commandfor=&quot;my-dialog&quot; command=&quot;show-modal&quot;&amp;gt;
    Show modal dialog
  &amp;lt;/button&amp;gt;

  &amp;lt;dialog id=&quot;my-dialog&quot;&amp;gt;
    &amp;lt;h3&amp;gt;Do you like modern Web APIs?&amp;lt;/h3&amp;gt;

    &amp;lt;div style=&quot;display:flex; gap:10px&quot;&amp;gt;
      &amp;lt;button commandfor=&quot;my-dialog&quot; command=&quot;close&quot; data-answer=&quot;yes&quot;&amp;gt;
        Yes
      &amp;lt;/button&amp;gt;

      &amp;lt;button commandfor=&quot;my-dialog&quot; command=&quot;close&quot; data-answer=&quot;sure&quot;&amp;gt;
        Sure
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/dialog&amp;gt;

  &amp;lt;p&amp;gt;No answer yet&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we listen for the &lt;code&gt;toggle&lt;/code&gt; event.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const paragraph = document.querySelector(&quot;p&quot;);

document.querySelector(&quot;dialog&quot;).addEventListener(&quot;toggle&quot;, (event) =&amp;gt; {

  if (!(event.source instanceof HTMLButtonElement)) return;

  const { answer } = event.source.dataset;

  if (answer) {
    paragraph.textContent = `Answer: ${answer}`;
  }

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;ToggleEvent.source&lt;/code&gt;, determining the trigger element becomes
trivial.&lt;/p&gt;
&lt;h2&gt;&amp;lt;Codepen id=&quot;019cbfd7-7afe-776d-afbe-461c3c60fb50&quot; /&amp;gt;&lt;/h2&gt;
&lt;h2&gt;HTMLDialogElement.closedBy&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/closedBy&quot;&gt;&lt;strong&gt;&lt;code&gt;closedby&lt;/code&gt; attribute&lt;/strong&gt;&lt;/a&gt; defines &lt;strong&gt;which user actions are allowed to
close a dialog&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Previously, developers often needed custom JavaScript logic to control
this behavior. Now it can be defined directly in HTML.&lt;/p&gt;
&lt;p&gt;This attribute is supported by all major browsers (Safari currently ships it behind an experimental flag).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/dialog-closedby.png&quot; alt=&quot;Browser support for dialog.closedBy&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Supported closing actions&lt;/h3&gt;
&lt;p&gt;Dialogs can be closed by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clicking outside the dialog on the overlay (light dismiss).&lt;/li&gt;
&lt;li&gt;Platform actions such as pressing &lt;strong&gt;Esc&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A developer-defined action such as a button calling
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close&quot;&gt;&lt;code&gt;dialog.close()&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;Attribute values&lt;/h2&gt;
&lt;h3&gt;any&lt;/h3&gt;
&lt;p&gt;The dialog can be closed using &lt;strong&gt;all methods&lt;/strong&gt; above.&lt;/p&gt;
&lt;h3&gt;closerequest&lt;/h3&gt;
&lt;p&gt;The dialog can be closed by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pressing &lt;strong&gt;Esc&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;developer-defined logic&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;none&lt;/h3&gt;
&lt;p&gt;The dialog can only be closed &lt;strong&gt;programmatically or by explicit UI
controls&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Default behavior&lt;/h2&gt;
&lt;p&gt;The default value depends on how the dialog is opened.&lt;/p&gt;
&lt;p&gt;If opened using: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal&quot;&gt;&lt;code&gt;showModal()&lt;/code&gt;&lt;/a&gt;, the default becomes: &lt;code&gt;closerequest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Otherwise the default is: &lt;code&gt;none&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Why this matters&lt;/h3&gt;
&lt;p&gt;Before &lt;code&gt;closedby&lt;/code&gt;, implementing overlay click closing required JavaScript logic, such as custom hooks (&lt;a href=&quot;https://jsdev.space/10-custom-react-hooks/&quot;&gt;&lt;code&gt;useClickOutside&lt;/code&gt;&lt;/a&gt; in React).&lt;/p&gt;
&lt;p&gt;Now this behavior can be defined purely declaratively.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Example with multiple dialogs&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;demo&quot;&amp;gt;
  &amp;lt;button class=&quot;open&quot; commandfor=&quot;dlg-any&quot; command=&quot;show-modal&quot;&amp;gt;
    Open dialog (closedby=&quot;any&quot;)
  &amp;lt;/button&amp;gt;

  &amp;lt;!-- 1) closedby=&quot;any&quot; --&amp;gt;
  &amp;lt;dialog id=&quot;dlg-any&quot; closedby=&quot;any&quot;&amp;gt;
    &amp;lt;div class=&quot;dialog&quot;&amp;gt;
      &amp;lt;header class=&quot;header&quot;&amp;gt;
        &amp;lt;h3&amp;gt;Dialog A — closedby=&quot;any&quot;&amp;lt;/h3&amp;gt;
        &amp;lt;button class=&quot;icon&quot; commandfor=&quot;dlg-any&quot; command=&quot;close&quot; aria-label=&quot;Close&quot;&amp;gt;
          &amp;amp;times;
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;p&amp;gt;
        This dialog can be closed by clicking the backdrop, pressing Esc, or using a Close button.
      &amp;lt;/p&amp;gt;

      &amp;lt;footer class=&quot;footer&quot;&amp;gt;
        &amp;lt;button class=&quot;cancel&quot; commandfor=&quot;dlg-any&quot; command=&quot;close&quot;&amp;gt;Close&amp;lt;/button&amp;gt;

        &amp;lt;button class=&quot;confirm&quot; commandfor=&quot;dlg-closerequest&quot; command=&quot;show-modal&quot;&amp;gt;
          Open dialog B
        &amp;lt;/button&amp;gt;
      &amp;lt;/footer&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/dialog&amp;gt;

  &amp;lt;!-- 2) closedby=&quot;closerequest&quot; --&amp;gt;
  &amp;lt;dialog id=&quot;dlg-closerequest&quot; closedby=&quot;closerequest&quot;&amp;gt;
    &amp;lt;div class=&quot;dialog&quot;&amp;gt;
      &amp;lt;header class=&quot;header&quot;&amp;gt;
        &amp;lt;h3&amp;gt;Dialog B — closedby=&quot;closerequest&quot;&amp;lt;/h3&amp;gt;
        &amp;lt;button class=&quot;icon&quot; commandfor=&quot;dlg-closerequest&quot; command=&quot;close&quot; aria-label=&quot;Close&quot;&amp;gt;
          &amp;amp;times;
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;p&amp;gt;
        This dialog closes via Esc or explicit controls. Clicking the backdrop should NOT close it.
      &amp;lt;/p&amp;gt;

      &amp;lt;footer class=&quot;footer&quot;&amp;gt;
        &amp;lt;button class=&quot;cancel&quot; commandfor=&quot;dlg-closerequest&quot; command=&quot;close&quot;&amp;gt;Close&amp;lt;/button&amp;gt;

        &amp;lt;button class=&quot;confirm&quot; commandfor=&quot;dlg-none&quot; command=&quot;show-modal&quot;&amp;gt;
          Open dialog C
        &amp;lt;/button&amp;gt;
      &amp;lt;/footer&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/dialog&amp;gt;

  &amp;lt;!-- 3) closedby=&quot;none&quot; --&amp;gt;
  &amp;lt;dialog id=&quot;dlg-none&quot; closedby=&quot;none&quot;&amp;gt;
    &amp;lt;div class=&quot;dialog&quot;&amp;gt;
…</content:encoded></item><item><title>How to Build an LRU Cache from Scratch in JavaScript</title><link>https://jsdev.space/howto/lru-cache-javascript/</link><guid isPermaLink="true">https://jsdev.space/howto/lru-cache-javascript/</guid><description>Learn how to implement an LRU cache in JavaScript from scratch using a Map and doubly linked list with O(1) operations. Includes step-by-step explanations and code.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Caching is a fundamental optimization technique used across nearly every modern software system. Whether you&apos;re building a web API, a database engine, or a frontend application, caching can dramatically improve performance by storing frequently accessed data in memory.&lt;/p&gt;
&lt;p&gt;One of the most common cache eviction strategies is LRU (Least Recently Used). You’ll encounter it in systems like Redis, operating systems, browser caches, and backend frameworks. It also appears frequently in technical interviews because implementing it correctly requires understanding both data structures and time complexity.&lt;/p&gt;
&lt;p&gt;Many developers initially find LRU confusing, especially because interview questions usually require O(1) time complexity for both retrieval and insertion operations. However, once you understand the right combination of data structures, the implementation becomes surprisingly elegant.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll build an LRU cache from scratch, following a clear progression:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Understanding what LRU actually means&lt;/li&gt;
&lt;li&gt;Defining the required operations&lt;/li&gt;
&lt;li&gt;Choosing the right data structures&lt;/li&gt;
&lt;li&gt;Implementing the cache step by step&lt;/li&gt;
&lt;li&gt;Testing the implementation&lt;/li&gt;
&lt;li&gt;Avoiding common mistakes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end, you&apos;ll have a production-ready LRU cache implementation in JavaScript.&lt;/p&gt;
&lt;h2&gt;What Is an LRU Cache?&lt;/h2&gt;
&lt;p&gt;LRU stands for &lt;strong&gt;Least Recently Used&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When the cache reaches its capacity, remove the item that hasn&apos;t been used for the longest time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every time an item is accessed or inserted, it becomes the &lt;strong&gt;most recently used&lt;/strong&gt; entry.&lt;/p&gt;
&lt;h2&gt;A Simple Real-World Analogy&lt;/h2&gt;
&lt;p&gt;Imagine a small bookshelf that can only hold three books.&lt;/p&gt;
&lt;p&gt;Capacity = 3&lt;/p&gt;
&lt;h3&gt;Step 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Add Algorithms
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Add Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Algorithms, Java]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3&lt;/h3&gt;
&lt;p&gt;Add Python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Algorithms, Java, Python]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The shelf is now full.&lt;/p&gt;
&lt;h3&gt;Step 4 – Access &quot;Algorithms&quot;&lt;/h3&gt;
&lt;p&gt;Because you used it recently, it moves to the end:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Java, Python, Algorithms]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 5 – Add &quot;JavaScript&quot;&lt;/h3&gt;
&lt;p&gt;Since the shelf is full, remove the least recently used item (&lt;strong&gt;Java&lt;/strong&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Python, Algorithms, JavaScript]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This behavior is exactly how an &lt;strong&gt;LRU cache works&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Core Requirements of an LRU Cache&lt;/h2&gt;
&lt;p&gt;In most implementations (and interviews), the cache supports two operations.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Required Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get(key)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return the value associated with the key, or &lt;code&gt;-1&lt;/code&gt; if not found. Also mark it as recently used.&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put(key, value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert or update a value. If capacity is exceeded, remove the least recently used item.&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;strong&gt;O(1)&lt;/strong&gt; requirement is critical.&lt;/p&gt;
&lt;p&gt;If operations become &lt;strong&gt;O(n)&lt;/strong&gt;, the implementation is no longer optimal.&lt;/p&gt;
&lt;h2&gt;Why Arrays Won’t Work&lt;/h2&gt;
&lt;p&gt;A naive solution might store items in an array.&lt;/p&gt;
&lt;p&gt;But arrays cause problems:&lt;/p&gt;
&lt;h3&gt;Finding an item&lt;/h3&gt;
&lt;p&gt;Requires scanning → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Moving an item&lt;/h3&gt;
&lt;p&gt;Requires shifting elements → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Removing the least used item&lt;/h3&gt;
&lt;p&gt;Also &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This violates the &lt;strong&gt;O(1)&lt;/strong&gt; requirement.&lt;/p&gt;
&lt;p&gt;So we need a better approach.&lt;/p&gt;
&lt;h2&gt;The Key Insight: Combine Two Data Structures&lt;/h2&gt;
&lt;p&gt;To achieve constant time operations, we combine:&lt;/p&gt;
&lt;h3&gt;1. Hash Map (Map)&lt;/h3&gt;
&lt;p&gt;Provides:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;key → node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instant lookup&lt;/li&gt;
&lt;li&gt;O(1) access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But a Map cannot track usage order.&lt;/p&gt;
&lt;h3&gt;2. Doubly Linked List&lt;/h3&gt;
&lt;p&gt;Maintains item order:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Least used  ←→  Most used
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fast removal&lt;/li&gt;
&lt;li&gt;Fast insertion&lt;/li&gt;
&lt;li&gt;Fast reordering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Operations on linked lists can be &lt;strong&gt;O(1)&lt;/strong&gt; if you already have the node reference.&lt;/p&gt;
&lt;h2&gt;Final Architecture&lt;/h2&gt;
&lt;p&gt;We combine both structures:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Map:
key → linked list node

Doubly Linked List:
[Least Recently Used ... Most Recently Used]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Rules&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Head = least recently used&lt;/li&gt;
&lt;li&gt;Tail = most recently used&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 1: Create a Linked List Node&lt;/h3&gt;
&lt;p&gt;Each node stores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key&lt;/li&gt;
&lt;li&gt;value&lt;/li&gt;
&lt;li&gt;previous pointer&lt;/li&gt;
&lt;li&gt;next pointer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class CacheNode {
  constructor(key, value) {
    this.key = key
    this.value = value
    this.prev = null
    this.next = null
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Important detail:&lt;/p&gt;
&lt;p&gt;We store the key inside the node so we can remove it from the Map during eviction.&lt;/p&gt;
&lt;h3&gt;Step 2: Implement a Doubly Linked List&lt;/h3&gt;
&lt;p&gt;We’ll use sentinel (dummy) nodes for easier boundary handling.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class DoublyLinkedList {
  constructor() {
    this.head = new CacheNode(null, null)
    this.tail = new CacheNode(null, null)

    this.head.next = this.tail
    this.tail.prev = this.head

    this.length = 0
  }

  remove(node) {
    node.prev.next = node.next
    node.next.prev = node.prev

    node.prev = null
    node.next = null

    this.length--
  }

  addToEnd(node) {
    const prevLast = this.tail.prev

    prevLast.next = node
    node.prev = prevLast
    node.next = this.tail
    this.tail.prev = node

    this.length++
  }

  removeFirst() {
    if (this.length === 0) return null

    const first = this.head.next
    this.remove(first)

    return first
  }

  size() {
    return this.length
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: Build the LRU Cache&lt;/h3&gt;
&lt;p&gt;Now we combine the Map and linked list.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class LRUCache {
  constructor(capacity) {
    if (capacity &amp;lt;= 0) {
      throw new Error(&quot;Capacity must be greater than zero&quot;)
    }

    this.capacity = capacity
    this.cache = new Map()
    this.list = new DoublyLinkedList()
  }

  get(key) {
    if (!this.cache.has(key)) {
      return -1
    }

    const node = this.cache.get(key)

    this.list.remove(node)
    this.list.addToEnd(node)

    return node.value
  }

  put(key, value) {
    if (this.cache.has(key)) {
      const node = this.cache.get(key)

      node.value = value

      this.list.remove(node)
      this.list.addToEnd(node)

      return
    }

    if (this.list.size() === this.capacity) {
      const removed = this.list.removeFirst()

      if (removed) {
        this.cache.delete(removed.key)
      }
    }

    const newNode = new CacheNode(key, value)

    this.list.addToEnd(newNode)
    this.cache.set(key, newNode)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Testing the Implementation&lt;/h3&gt;
&lt;p&gt;Let&apos;s verify the behavior.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function testLRUCache() {
  const cache = new LRUCache(3)

  cache.put(&quot;Algorithms&quot;, 1)
  cache.put(&quot;Java&quot;, 2)
  cache.put(&quot;Python&quot;, 3)

  console.log(cache.get(&quot;Algorithms&quot;))
  // 1

  cache.put(&quot;JavaScript&quot;, 4)

  console.log(cache.get(&quot;Java&quot;))
  // -1 (evicted)

  console.log(cache.get(&quot;JavaScript&quot;))
  // 4

  cache.put(&quot;Python&quot;, 33)

  console.log(cache.get(&quot;Python&quot;))
  // 33
}

testLRUCache()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the output matches expectations, the cache works correctly.&lt;/p&gt;
&lt;h2&gt;Common Mistakes Developers Make&lt;/h2&gt;
&lt;h3&gt;1. Forgetting to store the key in nodes&lt;/h3&gt;
&lt;p&gt;Without the key, eviction cannot remove the Map entry.&lt;/p&gt;
&lt;h3&gt;2. Using a singly linked list&lt;/h3&gt;
&lt;p&gt;Deleting nodes becomes &lt;strong&gt;O(n)&lt;/strong&gt; because the previous node must be searched.&lt;/p&gt;
&lt;h3&gt;3. Not using sentinel nodes&lt;/h3&gt;
&lt;p&gt;This causes many edge cases when inserting or deleting.&lt;/p&gt;
&lt;h3&gt;4. Forgetting to update the size counter&lt;/h3&gt;
&lt;p&gt;This breaks capacity checks.&lt;/p&gt;
&lt;h3&gt;5. Updating value without updating position&lt;/h3&gt;
&lt;p&gt;Accessing an item sho…</content:encoded></item><item><title>How to Build a Safe JSON Parser for Node.js APIs</title><link>https://jsdev.space/howto/safe-json-parser-nodejs/</link><guid isPermaLink="true">https://jsdev.space/howto/safe-json-parser-nodejs/</guid><description>Learn how to build a safe JSON parser wrapper for Node.js APIs with payload limits, schema validation, dangerous key filtering, and production-ready error handling.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Parsing JSON in Node.js looks trivial at first glance.&lt;/p&gt;
&lt;p&gt;Most developers start with something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = JSON.parse(rawBody)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And for a while, that feels good enough.&lt;/p&gt;
&lt;p&gt;Then real traffic arrives.&lt;/p&gt;
&lt;p&gt;A client sends malformed JSON. A webhook includes fields you did not expect. A bot posts a giant payload. A malicious user tries to poison object prototypes. Suddenly, one innocent-looking &lt;code&gt;JSON.parse()&lt;/code&gt; call turns into a source of crashes, broken validation, or security issues.&lt;/p&gt;
&lt;p&gt;That is why production APIs should not parse JSON blindly.&lt;/p&gt;
&lt;p&gt;A safer approach is to build a JSON parser wrapper that handles the boring but critical work for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reject oversized payloads&lt;/li&gt;
&lt;li&gt;block dangerous keys&lt;/li&gt;
&lt;li&gt;validate structure at runtime&lt;/li&gt;
&lt;li&gt;return consistent errors&lt;/li&gt;
&lt;li&gt;integrate cleanly with Express, Fastify, Hono, or custom Node.js servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, we will build a reusable safe JSON parser wrapper for Node.js APIs, explain the design decisions behind it, and finish with production-ready examples you can adapt to your own backend.&lt;/p&gt;
&lt;h2&gt;Why a Wrapper Is Better Than Plain JSON.parse()&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; is not bad. It is fast, built-in, and perfectly fine when the input is trusted.&lt;/p&gt;
&lt;p&gt;The real problem is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In APIs, input is often untrusted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;request bodies&lt;/li&gt;
&lt;li&gt;webhooks&lt;/li&gt;
&lt;li&gt;uploaded JSON files&lt;/li&gt;
&lt;li&gt;queue messages&lt;/li&gt;
&lt;li&gt;Redis payloads&lt;/li&gt;
&lt;li&gt;database JSON blobs&lt;/li&gt;
&lt;li&gt;third-party callbacks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When raw input comes from outside your system, parsing should do more than just convert a string into an object.&lt;/p&gt;
&lt;p&gt;A proper parser layer should answer these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the payload too large?&lt;/li&gt;
&lt;li&gt;Is the JSON valid?&lt;/li&gt;
&lt;li&gt;Does it contain suspicious keys?&lt;/li&gt;
&lt;li&gt;Does it match the shape the API expects?&lt;/li&gt;
&lt;li&gt;Can the application return a clean error instead of crashing?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is exactly what a wrapper solves.&lt;/p&gt;
&lt;h2&gt;What a Safe JSON Parser Should Do&lt;/h2&gt;
&lt;p&gt;A strong implementation should provide five protections.&lt;/p&gt;
&lt;h3&gt;1. Type checks&lt;/h3&gt;
&lt;p&gt;Only strings or buffers should be accepted as raw JSON input.&lt;/p&gt;
&lt;h3&gt;2. Payload size limits&lt;/h3&gt;
&lt;p&gt;This helps reduce DoS risk and prevents huge request bodies from consuming memory.&lt;/p&gt;
&lt;h3&gt;3. Dangerous key filtering&lt;/h3&gt;
&lt;p&gt;Keys like &lt;code&gt;__proto__&lt;/code&gt;, &lt;code&gt;constructor&lt;/code&gt;, and &lt;code&gt;prototype&lt;/code&gt; can become a problem in unsafe merge flows.&lt;/p&gt;
&lt;h3&gt;4. Runtime validation&lt;/h3&gt;
&lt;p&gt;Even valid JSON is not necessarily valid application data.&lt;/p&gt;
&lt;h3&gt;5. Consistent error handling&lt;/h3&gt;
&lt;p&gt;The API should return predictable, human-readable errors instead of generic crashes.&lt;/p&gt;
&lt;h2&gt;The Core Design&lt;/h2&gt;
&lt;p&gt;We will build the wrapper in layers.&lt;/p&gt;
&lt;p&gt;First, a small custom error class.
Then a secure parser with size checks and key filtering.
Then a schema-aware parser using Zod.
Finally, an Express middleware example.&lt;/p&gt;
&lt;p&gt;This approach keeps the code modular and easy to test.&lt;/p&gt;
&lt;h3&gt;Step 1: Create Custom Error Types&lt;/h3&gt;
&lt;p&gt;A parser wrapper becomes much easier to integrate when it throws structured errors instead of generic &lt;code&gt;Error&lt;/code&gt; objects.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export class JsonParseError extends Error {
  public readonly statusCode: number
  public readonly code: string
  public readonly details?: unknown

  constructor(message: string, options?: {
    statusCode?: number
    code?: string
    details?: unknown
  }) {
    super(message)
    this.name = &quot;JsonParseError&quot;
    this.statusCode = options?.statusCode ?? 400
    this.code = options?.code ?? &quot;INVALID_JSON&quot;
    this.details = options?.details
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the rest of the application useful metadata:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;statusCode&lt;/code&gt; for HTTP responses&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code&lt;/code&gt; for machine-readable error handling&lt;/li&gt;
&lt;li&gt;&lt;code&gt;details&lt;/code&gt; for debugging or validation reports&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 2: Define Safe Defaults&lt;/h3&gt;
&lt;p&gt;Now define the parser configuration.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export type SafeJsonParserOptions = {
  maxBytes?: number
  forbiddenKeys?: string[]
}

const DEFAULT_FORBIDDEN_KEYS = [&quot;__proto__&quot;, &quot;constructor&quot;, &quot;prototype&quot;]

const DEFAULT_OPTIONS: Required&amp;lt;SafeJsonParserOptions&amp;gt; = {
  maxBytes: 100 * 1024,
  forbiddenKeys: DEFAULT_FORBIDDEN_KEYS,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These defaults are intentionally conservative.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;100KB&lt;/code&gt; default is reasonable for many APIs, but you can raise or lower it depending on your use case.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;small JSON commands: 10KB&lt;/li&gt;
&lt;li&gt;standard API forms: 50KB to 200KB&lt;/li&gt;
&lt;li&gt;large content payloads: 500KB to 1MB&lt;/li&gt;
&lt;li&gt;file uploads: do not parse with raw &lt;code&gt;JSON.parse()&lt;/code&gt; at all&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 3: Normalize Raw Input&lt;/h3&gt;
&lt;p&gt;In Node.js, raw request data may arrive as a string or a buffer.&lt;/p&gt;
&lt;p&gt;A wrapper should handle both cleanly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function normalizeInput(raw: string | Buffer): string {
  if (typeof raw === &quot;string&quot;) {
    return raw
  }

  if (Buffer.isBuffer(raw)) {
    return raw.toString(&quot;utf8&quot;)
  }

  throw new JsonParseError(&quot;Unsupported input type&quot;, {
    code: &quot;INVALID_INPUT_TYPE&quot;,
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents accidental misuse and keeps the main parser logic simpler.&lt;/p&gt;
&lt;h3&gt;Step 4: Enforce a Payload Size Limit&lt;/h3&gt;
&lt;p&gt;Before calling &lt;code&gt;JSON.parse()&lt;/code&gt;, check size first.&lt;/p&gt;
&lt;p&gt;This is one of the easiest and most effective hardening steps.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function assertPayloadSize(raw: string, maxBytes: number): void {
  const byteLength = Buffer.byteLength(raw, &quot;utf8&quot;)

  if (byteLength &amp;gt; maxBytes) {
    throw new JsonParseError(
      `JSON payload exceeds the ${maxBytes} byte limit`,
      {
        statusCode: 413,
        code: &quot;PAYLOAD_TOO_LARGE&quot;,
        details: { maxBytes, actualBytes: byteLength },
      }
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why check bytes instead of &lt;code&gt;string.length&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Because network payloads are measured in bytes, not characters. UTF-8 characters can take more than one byte, so &lt;code&gt;Buffer.byteLength()&lt;/code&gt; is the safer choice.&lt;/p&gt;
&lt;h3&gt;Step 5: Reject Dangerous Keys During Parsing&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;reviver&lt;/code&gt; argument of &lt;code&gt;JSON.parse()&lt;/code&gt; lets you inspect every key-value pair during parsing.&lt;/p&gt;
&lt;p&gt;That makes it a good place to block suspicious keys.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function createSecureReviver(forbiddenKeys: string[]) {
  const blocked = new Set(forbiddenKeys)

  return function secureReviver(key: string, value: unknown) {
    if (blocked.has(key)) {
      throw new JsonParseError(`Forbidden key found in JSON: ${key}`, {
        code: &quot;FORBIDDEN_JSON_KEY&quot;,
        details: { key },
      })
    }

    return value
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does not magically solve all object safety issues across your application, but it reduces risk significantly when dealing with untrusted payloads.&lt;/p&gt;
&lt;h3&gt;Step 6: Build the Base Safe Parser&lt;/h3&gt;
&lt;p&gt;Now combine the pieces into one reusable function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { JsonParseError } from &quot;./json-parse-error&quot;

export type SafeJsonParserOptions = {
  maxBytes?: number
  forbiddenKeys?: string[]
}

const DEFAULT_FORBIDDEN_KEYS = [&quot;__proto__&quot;, &quot;constructor&quot;, &quot;prototype&quot;]

const DEFAULT_OPTIONS: Required&amp;lt;SafeJsonParserOptions&amp;gt; = {
  maxBytes: 100 * 1024,
  forbiddenKeys: DEFAULT_FORBIDDEN_KEYS,
}

function normalizeInput(raw: string | Buffer): string {
  if (typeof raw === &quot;string&quot;) {
    return raw
  }

  if (Buffer.isBuffer(raw)) {
    return raw.toString(&quot;utf8&quot;)
  }

  throw new JsonParseError(&quot;Unsupported input type&quot;, {
    code: &quot;INVALID_INPUT_TYPE&quot;,
  })
}

function assertPayloadSize(raw: string, maxBytes: number): void {
  const byteLength = Buffer.byteLength(raw, &quot;utf8&quot;)

  if (byteLength &amp;gt; maxBytes) {
    throw new JsonParseError(
      `JSON payload exceeds the ${maxBytes} byte limit`,
      {
     …</content:encoded></item><item><title>Friday Links #35: Dev Tools, AI &amp; JS Ecosystem Updates</title><link>https://jsdev.space/friday/friday-35/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-35/</guid><description>Discover the latest JavaScript tools, AI dev platforms, frameworks, and ecosystem updates in this week’s curated Friday Links roundup.</description><pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-35.png&quot; alt=&quot;Friday Links #35&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another week, another wave of interesting releases across the JavaScript ecosystem. From emerging developer tools and AI-powered workflows to framework updates and experimental projects, the pace of innovation keeps accelerating.&lt;/p&gt;
&lt;p&gt;In Friday Links #35, we’ve gathered the most notable discoveries worth a developer’s attention — tools that improve productivity, libraries pushing frontend boundaries, and projects that might quietly become tomorrow’s standards.&lt;/p&gt;
&lt;p&gt;Whether you&apos;re building production apps, experimenting with AI tooling, or just staying current with modern web development, this week’s picks have something valuable to explore.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/blog/agentic-future&quot;&gt;Building Next.js for an agentic future&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://adventures.nodeland.dev/archive/yes-learning-to-code-is-still-valuable&quot;&gt;Yes, Learning to Code Is Still Valuable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/potentially-coming-to-a-browser-near-you/&quot;&gt;Potentially Coming to a Browser :near() You &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/death-to-scroll-fade/&quot;&gt;Death to Scroll Fade!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.logrocket.com/react-server-components-performance-mistakes/&quot;&gt;6 React Server Component performance pitfalls in Next.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.hyperparam.app/hightable-scrolling-billions-of-rows/&quot;&gt;Virtual Scrolling for Billions of Rows — Techniques from HighTable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://thecodebarbarian.com/getting-started-with-the-vercel-ai-sdk-in-nodejs.html&quot;&gt;Getting Started with the Vercel AI SDK in Node.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackinsight.dev/blog/loop-performance-empirical-study&quot;&gt;Loop Performance Anti-Patterns: A 40-Repository Scan and Six-Module Benchmark Study&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aarontgrogg.github.io/NoLoJS/&quot;&gt;Reduce the JS Workload with no- or lo-JS options&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stephaniewalter.design/blog/tips-on-how-to-pick-the-right-icons-for-your-website-with-icons8/&quot;&gt;Tips on How to Pick the Right Icons for Your Website&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals&quot;&gt;AGENTS.md outperforms skills in our agent evals&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://una.im/border-shape&quot;&gt;border-shape: the future of the non-rectangular web&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://piccalil.li/blog/an-in-depth-guide-to-customising-lists-with-css/&quot;&gt;An in-depth guide to customising lists with CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/loading-smarter-svg-vs-raster-loaders-in-modern-web-design/&quot;&gt;Loading Smarter: SVG vs. Raster Loaders in Modern Web Design&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.react.doctor/&quot;&gt;React Doctor&lt;/a&gt; - is an open-source CLI tool created by the Million.js (millionco) team that scans React codebases and automatically detects common issues: anti-patterns, performance bottlenecks, accessibility gaps, architectural flaws, and even critical security vulnerabilities that can quietly slip into production.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://svar.dev/react/gantt/&quot;&gt;SVAR React Gantt&lt;/a&gt; - is a modern, high-performance Gantt chart component designed specifically for React applications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://llm-timeline.com/&quot;&gt;LLM Timeline&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://oxc.rs/&quot;&gt;The JavaScript Oxidation Compiler&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://batiste.github.io/blop/example/&quot;&gt;Blop&lt;/a&gt; - A typed language for the web that compiles to Virtual DOM. Blop uses real control flow statements — for loops, if/else — directly in templates, without JSX constraints.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sciter.com/&quot;&gt;Sciter&lt;/a&gt; – Embeddable HTML/CSS/JavaScript Engine for modern UI development&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/taskforcesh/bullmq&quot;&gt;BullMQ&lt;/a&gt; - Message Queue and Batch processing for NodeJS, Python, Elixir and PHP based on Redis&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tanstack/hotkeys&quot;&gt;TanStack Hotkeys&lt;/a&gt; -  Type-Safe keyboard shortcuts library with awesome devtools&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Mina-Massoud/Mina-Rich-Editor&quot;&gt;Mina Rich Editor&lt;/a&gt; - A powerful, elegant rich text editor built with Shadcn UI. Experience unparalleled customization, beautiful design, and seamless integration. Built with React, TypeScript, and meticulous attention to detail.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/Temporal-Playground.png&quot; alt=&quot;Temporal Playground&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://temporal-playground.vercel.app/&quot;&gt;Temporal Playground&lt;/a&gt; — an interactive online environment for experimenting with the JavaScript Temporal API, allowing developers to run real code and explore modern date and time handling directly in the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nicotsx/zerobyte&quot;&gt;Zerobyte&lt;/a&gt; - Powerful backup automation for your remote storage&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/LayoutitStudio/voxcss&quot;&gt;voxcss&lt;/a&gt; -  A CSS voxel engine. A 3D grid for the DOM. Renders HTML cuboids by stacking grid layers and applying transforms.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel-labs/portless&quot;&gt;portless&lt;/a&gt; - Replace port numbers with stable, named .localhost URLs. For humans and agents.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tomkp/react-split-pane&quot;&gt;react-split-pane&lt;/a&gt; -  React split-pane component&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/patrickjuchli/basic-ftp&quot;&gt;Basic FTP&lt;/a&gt; -  FTP client for Node.js, supports FTPS over TLS, passive mode over IPv6, async/await, and Typescript.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sathvikc/lume-js&quot;&gt;Lume.js&lt;/a&gt; - Minimal reactive state management using only standard JavaScript and HTML. No custom syntax, no build step required, no framework lock-in.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/superlucky84/fp-pack&quot;&gt;fp-pack&lt;/a&gt; -  A functional toolkit focused on type-safe pipelines, not FP dogma, for JavaScript and TypeScript.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tambo-ai/tambo&quot;&gt;tambo&lt;/a&gt; -  Generative UI SDK for React&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://biomejs.dev/blog/biome-v2-4/&quot;&gt;Biome v2.4—Embedded Snippets, HTML Accessibility, and Better Framework Support&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/prisma/prisma/releases/tag/7.4.0&quot;&gt;Prsma 7.4.0 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://phaser.io/news/2026/02/phaser-editor-v5-beta&quot;&gt;Phaser Editor v5 Beta now available&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#502---022526&quot;&gt;Emscripten 5.0.2&lt;/a&gt; — the well-established LLVM-to-WebAssembly compiler that enables running native low-level code in Node.js without native bindings — receives internal cleanups, removing legacy Node-specific workarounds that are no longer required.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/honojs/hono/releases/tag/v4.12.0&quot;&gt;Hono 4.12&lt;/a&gt; — a lightweight, multi-runtime web framework built around Web Standards, designed to run seamlessly across Node.js, Bun, Deno, Cloudflare Workers, and other edge environments.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/alfateam/orange-orm/releases/tag/v5.2.0&quot;&gt;Orange ORM 5.2&lt;/a&gt; — a powerful and modern ORM designed for efficient database interaction, offering type-safe queries, clean abstractions, and strong performance across modern JavaScript runtimes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://eslint.org/blog/2026/02/eslint-v10.0.2-released/&quot;&gt;ESLint 10.0.2 Released&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📺 Videos&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ab01W6h4Giw&quot;&gt;TanStack Router - How to Become a Routing God in React&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0G_HKDrYpYc&quot;&gt;Build Your Own Claude Code with Mastra Workspaces&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uGsauG7Btlg&quot;&gt;Build &amp;amp; Deploy AI Agent Workflow Builder using NextJs, Mongodb, React, Prisma, Upstash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=O7DTIHISrJw&quot;&gt;How One Engineer and AI Crashed IBM&apos;s Stock Price&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ssYt09bCgUY&quot;&gt;The wild rise of OpenClaw...&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=bzWI3Dil9Ig&quot;&gt;My Multi-Agent Team with OpenClaw&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v…</content:encoded></item><item><title>SOLID Principles in React: SRP and OCP</title><link>https://jsdev.space/react-solid-srp-ocp/</link><guid isPermaLink="true">https://jsdev.space/react-solid-srp-ocp/</guid><description>Learn how Single Responsibility and Open Closed principles apply to React components, hooks, and scalable frontend architecture.</description><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;SOLID Principles in React Architecture&lt;/h2&gt;
&lt;p&gt;Software architecture almost never collapses instantly.&lt;/p&gt;
&lt;p&gt;Most React applications begin in a perfectly reasonable state.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A few components.&lt;/li&gt;
&lt;li&gt;Some hooks.&lt;/li&gt;
&lt;li&gt;Clean folders.&lt;/li&gt;
&lt;li&gt;Readable logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything feels simple.&lt;/p&gt;
&lt;p&gt;Then reality arrives.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New product requirements appear.&lt;/li&gt;
&lt;li&gt;APIs evolve.&lt;/li&gt;
&lt;li&gt;Design variants multiply.&lt;/li&gt;
&lt;li&gt;State management spreads across the application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And suddenly something strange happens:&lt;/p&gt;
&lt;p&gt;Changing one feature unexpectedly breaks three unrelated screens.&lt;/p&gt;
&lt;p&gt;At this point developers usually blame React, state management, or framework decisions. But the real problem is almost always architectural.&lt;/p&gt;
&lt;p&gt;The system stopped respecting &lt;strong&gt;responsibilities&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Read more: &lt;a href=&quot;https://jsdev.space/solid-design-principles/&quot;&gt;Understanding SOLID Principles&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why SOLID Still Matters in React&lt;/h2&gt;
&lt;p&gt;SOLID principles were originally introduced for object-oriented programming by Robert C. Martin.&lt;/p&gt;
&lt;p&gt;React is not object-oriented in the classical sense.&lt;/p&gt;
&lt;p&gt;Yet modern React applications map surprisingly well to SOLID ideas:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OOP Concept&lt;/th&gt;
&lt;th&gt;React Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;Hook / handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency&lt;/td&gt;
&lt;td&gt;Service / API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composition&lt;/td&gt;
&lt;td&gt;Component composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abstraction&lt;/td&gt;
&lt;td&gt;Hook interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;React didn’t remove architecture problems.&lt;/p&gt;
&lt;p&gt;It simply changed where they appear.&lt;/p&gt;
&lt;p&gt;Let’s explore the three SOLID principles that have the biggest real-world impact in React systems.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Single Responsibility Principle (SRP) in React&lt;/h2&gt;
&lt;p&gt;Software architecture often sounds abstract until a project starts
growing. Messy components, duplicated logic, and unpredictable bugs
begin to appear.&lt;/p&gt;
&lt;p&gt;The Single Responsibility Principle states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A module should have only one reason to change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In React, responsibilities usually split into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;data logic&lt;/li&gt;
&lt;li&gt;rendering&lt;/li&gt;
&lt;li&gt;composition&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SRP &lt;strong&gt;does not mean&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one function&lt;/li&gt;
&lt;li&gt;small component&lt;/li&gt;
&lt;li&gt;minimal lines of code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, it means:&lt;/p&gt;
&lt;p&gt;A module should change for &lt;strong&gt;one category of reason&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Bad Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function AccountProfile() {
  const [profile, setProfile] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() =&amp;gt; {
    fetch(&quot;/api/profile&quot;)
      .then(res =&amp;gt; res.json())
      .then(data =&amp;gt; {
        setProfile(data);
        setLoading(false);
      });
  }, []);

  if (loading) return &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;;

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;{profile.name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{profile.email}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Component responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fetching data&lt;/li&gt;
&lt;li&gt;managing state&lt;/li&gt;
&lt;li&gt;rendering UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Multiple reasons to change.&lt;/p&gt;
&lt;h3&gt;Correct SRP Architecture&lt;/h3&gt;
&lt;p&gt;A scalable React architecture separates &lt;strong&gt;logic from presentation&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Data Hook&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function useProfileData() {
  const [profile, setProfile] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() =&amp;gt; {
    fetch(&quot;/api/profile&quot;)
      .then(res =&amp;gt; res.json())
      .then(setProfile)
      .finally(() =&amp;gt; setLoading(false));
  }, []);

  return { profile, loading };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This hook answers one question:&lt;/p&gt;
&lt;p&gt;How do we obtain data?&lt;/p&gt;
&lt;p&gt;Nothing else.&lt;/p&gt;
&lt;h3&gt;View Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ProfileView({ profile, loading }) {
  if (loading) return &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;;

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;{profile.name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{profile.email}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pure UI.&lt;/p&gt;
&lt;p&gt;No knowledge about APIs or storage.&lt;/p&gt;
&lt;h3&gt;Container&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ProfileContainer() {
  const state = useProfileData();
  return &amp;lt;ProfileView {...state} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now each part has one responsibility.&lt;/p&gt;
&lt;p&gt;Changing backend logic never affects rendering.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Real SRP Benefit&lt;/h2&gt;
&lt;p&gt;SRP enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;safer refactoring&lt;/li&gt;
&lt;li&gt;reusable UI components&lt;/li&gt;
&lt;li&gt;independent testing&lt;/li&gt;
&lt;li&gt;predictable scaling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Large React applications survive long-term mainly because of this separation.&lt;/p&gt;
&lt;h2&gt;Open Closed Principle (OCP) in React&lt;/h2&gt;
&lt;p&gt;The Open Closed Principle states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Software entities should be open for extension but closed for
modification.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You should add new behavior without rewriting existing components.&lt;/p&gt;
&lt;h3&gt;Typical Violation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ActionButton({ type, onClick }) {
  if (type === &quot;primary&quot;) {
    return &amp;lt;button className=&quot;primary&quot; onClick={onClick}&amp;gt;Primary&amp;lt;/button&amp;gt;;
  }

  if (type === &quot;danger&quot;) {
    return &amp;lt;button className=&quot;danger&quot; onClick={onClick}&amp;gt;Danger&amp;lt;/button&amp;gt;;
  }

  if (type === &quot;success&quot;) {
    return &amp;lt;button className=&quot;success&quot; onClick={onClick}&amp;gt;Success&amp;lt;/button&amp;gt;;
  }

  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every new variation requires modifying the component.&lt;/p&gt;
&lt;p&gt;Over time this becomes fragile and error-prone.&lt;/p&gt;
&lt;h3&gt;OCP-Friendly Approach&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const buttonVariants = {
  primary: &quot;primary&quot;,
  danger: &quot;danger&quot;,
  success: &quot;success&quot;,
};

export function ActionButton({ variant, onClick, children }) {
  return (
    &amp;lt;button
      className={buttonVariants[variant]}
      onClick={onClick}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding new behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;buttonVariants.warning = &quot;warning&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No component modification required.&lt;/p&gt;
&lt;h3&gt;Composition-Based Extension&lt;/h3&gt;
&lt;p&gt;Modern React strongly favors composition.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function Button({ className, ...props }) {
  return &amp;lt;button className={className} {...props} /&amp;gt;;
}

export function DangerButton(props) {
  return &amp;lt;Button className=&quot;danger&quot; {...props} /&amp;gt;;
}

export function PrimaryButton(props) {
  return &amp;lt;Button className=&quot;primary&quot; {...props} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base abstraction remains stable while functionality grows externally.&lt;/p&gt;
&lt;p&gt;This is OCP applied naturally.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Dependency Inversion Principle (DIP)&lt;/h2&gt;
&lt;p&gt;This principle separates scalable architecture from tightly coupled applications.&lt;/p&gt;
&lt;p&gt;Definition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;High-level modules should not depend on low-level modules.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Translated to React:&lt;/p&gt;
&lt;p&gt;UI should not depend directly on infrastructure.&lt;/p&gt;
&lt;h2&gt;Hidden Dependency Problem&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;export function OrdersPage() {
  const [orders, setOrders] = useState([]);

  useEffect(() =&amp;gt; {
    fetch(&quot;/api/orders&quot;)
      .then(r =&amp;gt; r.json())
      .then(setOrders);
  }, []);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component depends directly on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;transport layer&lt;/li&gt;
&lt;li&gt;backend structure&lt;/li&gt;
&lt;li&gt;API implementation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Backend change → UI rewrite.&lt;/p&gt;
&lt;h2&gt;Introducing Abstraction&lt;/h2&gt;
&lt;h3&gt;Provider Interface&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export interface OrdersProvider {
  getOrders(): Promise&amp;lt;any[]&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Concrete Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export class ApiOrdersProvider implements OrdersProvider {
  async getOrders() {
    const res = await fetch(&quot;/api/orders&quot;);
    return res.json();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Hook Depends on Abstraction&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function useOrders(provider: OrdersProvider) {
  const [orders, setOrders] = useState([]);

  useEffect(() =&amp;gt; {
    provider.getOrders().then(setOrders);
  }, [provider]);

  return orders;
}
&lt;/code&gt;&lt;/pr…</content:encoded></item><item><title>SQL Crash Course: JOINs, CTEs, and Window Functions</title><link>https://jsdev.space/sql-complete-guide/</link><guid isPermaLink="true">https://jsdev.space/sql-complete-guide/</guid><description>A complete modern SQL guide covering SELECT, filtering, sorting, aggregates, JOINs, DML/DDL, CTEs, execution order, indexes, and window functions.</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;SQL is one of the very few technologies in software engineering that
does not fade with trends.&lt;/p&gt;
&lt;p&gt;Frameworks change every few years. Backend stacks rotate. Frontend
ecosystems reinvent themselves. But SQL stays.&lt;/p&gt;
&lt;p&gt;That is not nostalgia. That is infrastructure.&lt;/p&gt;
&lt;p&gt;SQL became the universal language of data. Whether a team uses
PostgreSQL, MySQL, ClickHouse, Snowflake, or something distributed and
exotic --- chances are, they still speak SQL.&lt;/p&gt;
&lt;p&gt;Understanding SQL today is not optional for serious developers.&lt;/p&gt;
&lt;p&gt;Backend engineers need it to avoid pushing database logic into
application code. Analysts rely on it daily. QA engineers use it to
validate system state. Product managers use it to inspect metrics
without waiting for analytics pipelines.&lt;/p&gt;
&lt;p&gt;This article walks through SQL step by step --- from simple SELECT
queries to advanced window functions --- using practical examples and a
PostgreSQL‑friendly syntax.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Database Anatomy: What We Actually Work With&lt;/h2&gt;
&lt;p&gt;Before writing queries, it helps to understand what the database really is.&lt;/p&gt;
&lt;p&gt;A relational database is simply structured tables connected by rules. Think of it as Excel with strict discipline — no messy types, no broken references, no silent mistakes.&lt;/p&gt;
&lt;p&gt;Each table contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Columns (structure)&lt;/li&gt;
&lt;li&gt;Rows (actual data)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The power comes from constraints and relationships.&lt;/p&gt;
&lt;p&gt;Unlike spreadsheets, relational databases enforce:&lt;/p&gt;
&lt;p&gt;. Strong typing
. Explicit relationships&lt;/p&gt;
&lt;h3&gt;Primary Keys and Foreign Keys&lt;/h3&gt;
&lt;p&gt;Primary Key (PK) --- unique row identifier.&lt;/p&gt;
&lt;p&gt;Foreign Key (FK) --- reference to another table&apos;s primary key.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;users(id, name)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orders(id, user_id, total)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;orders.user_id&lt;/code&gt; references &lt;code&gt;users.id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This guarantees consistency: the database will reject an order for a
non‑existent user.&lt;/p&gt;
&lt;h3&gt;Common Data Types&lt;/h3&gt;
&lt;p&gt;Most projects rely on a core set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;INT / BIGINT --- numeric identifiers and counters&lt;/li&gt;
&lt;li&gt;TEXT / VARCHAR --- strings&lt;/li&gt;
&lt;li&gt;TIMESTAMPTZ --- date-time stored in UTC&lt;/li&gt;
&lt;li&gt;JSONB (PostgreSQL) --- flexible semi‑structured data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Always store timestamps in UTC. Convert on the client side.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Reading Data: SELECT&lt;/h2&gt;
&lt;p&gt;The most common SQL operation is reading.&lt;/p&gt;
&lt;p&gt;Basic query:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, price
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avoid:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why? Because production systems scale. Fetching unnecessary columns wastes bandwidth, memory, and sometimes performance advantages from indexes.&lt;/p&gt;
&lt;h3&gt;Aliases&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  name AS product_name,
  price AS product_price
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is especially helpful in complex JOIN queries.&lt;/p&gt;
&lt;h3&gt;DISTINCT&lt;/h3&gt;
&lt;p&gt;Use DISTINCT when you care about unique values, not raw rows.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT DISTINCT category
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pagination&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT id, name
FROM products
ORDER BY id
LIMIT 10 OFFSET 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Large OFFSET values are inefficient. Prefer keyset pagination in
high‑load systems:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT id, name
FROM products
WHERE id &amp;gt; 1000
ORDER BY id
LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OFFSET works for small pages. For large datasets, keyset pagination is more efficient.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Filtering and Sorting&lt;/h2&gt;
&lt;p&gt;Filtering uses WHERE.&lt;/p&gt;
&lt;p&gt;Filtering narrows data down to what actually matters.&lt;/p&gt;
&lt;p&gt;Instead of loading everything and filtering in application code, push logic into the database — it is optimized for this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, price
FROM products
WHERE category = &apos;phones&apos;
  AND price &amp;gt; 50000;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;IN&lt;/h3&gt;
&lt;p&gt;Cleaner than multiple OR statements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE category IN (&apos;phones&apos;, &apos;laptops&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;BETWEEN&lt;/h3&gt;
&lt;p&gt;Readable range filter.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE price BETWEEN 30000 AND 60000
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;LIKE / ILIKE&lt;/h3&gt;
&lt;p&gt;Pattern matching for text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE name LIKE &apos;iPhone%&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Postgres case-insensitive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE name ILIKE &apos;iphone%&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;NULL Handling&lt;/h3&gt;
&lt;p&gt;NULL means “unknown”, not empty.&lt;/p&gt;
&lt;p&gt;Incorrect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE description = NULL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE description IS NULL
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ORDER BY&lt;/h3&gt;
&lt;p&gt;Sorting is expensive on large datasets — especially without indexes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, price
FROM products
ORDER BY price DESC, name ASC;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sorting large datasets without indexes is expensive.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Aggregation and GROUP BY&lt;/h2&gt;
&lt;p&gt;Aggregation turns raw data into insight.&lt;/p&gt;
&lt;p&gt;Instead of looking at thousands of rows, we compute metrics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/aggregation-css.png&quot; alt=&quot;Aggregation and GROUP BY&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Aggregate functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COUNT&lt;/li&gt;
&lt;li&gt;SUM&lt;/li&gt;
&lt;li&gt;AVG&lt;/li&gt;
&lt;li&gt;MIN&lt;/li&gt;
&lt;li&gt;MAX&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  MAX(price) AS max_price,
  AVG(price) AS avg_price,
  COUNT(*) AS total_products
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GROUP BY&lt;/h3&gt;
&lt;p&gt;Grouping splits rows into logical buckets before aggregation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  category,
  COUNT(*) AS product_count,
  AVG(price) AS avg_price
FROM products
GROUP BY category;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rule:&lt;/p&gt;
&lt;p&gt;Every non-aggregated column in SELECT must appear in GROUP BY.&lt;/p&gt;
&lt;h3&gt;HAVING&lt;/h3&gt;
&lt;p&gt;HAVING filters groups after aggregation — unlike WHERE.&lt;/p&gt;
&lt;p&gt;Incorrect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT category, COUNT(*)
FROM products
WHERE COUNT(*) &amp;gt; 10
GROUP BY category;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT category, COUNT(*)
FROM products
GROUP BY category
HAVING COUNT(*) &amp;gt; 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. JOINs&lt;/h2&gt;
&lt;p&gt;Real SQL begins with JOINs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/sql-joins.png&quot; alt=&quot;JOINs in SQL&quot; /&gt;&lt;/p&gt;
&lt;p&gt;JOINs connect tables. This is where relational databases shine.&lt;/p&gt;
&lt;p&gt;Instead of duplicating data, we combine normalized structures dynamically.&lt;/p&gt;
&lt;h3&gt;INNER JOIN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only matching rows are returned.&lt;/p&gt;
&lt;h3&gt;LEFT JOIN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All users appear. Missing matches become NULL.&lt;/p&gt;
&lt;p&gt;Find users without orders:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SELF JOIN&lt;/h3&gt;
&lt;p&gt;Used when a table references itself (hierarchies, managers, categories).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT e.name AS employee,
       m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Data Modification (DML)&lt;/h2&gt;
&lt;p&gt;These queries change actual data.&lt;/p&gt;
&lt;p&gt;Use carefully.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/data-modification.png&quot; alt=&quot;Data Modification&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;INSERT&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO users (name, email)
VALUES (&apos;Alex&apos;, &apos;alex@example.com&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Always list columns.&lt;/p&gt;
&lt;h3&gt;UPDATE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;UPDATE products
SET price = 65000
WHERE id = 42;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Never run UPDATE without WHERE unless intentional.&lt;/p&gt;
&lt;h3&gt;DELETE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;DELETE FROM users
WHERE last_login &amp;lt; &apos;2020-01-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TRUNCATE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TRUNCATE TABLE logs;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instantly removes all rows.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Schema Changes (DDL)&lt;/h2&gt;
&lt;p&gt;DDL modifies structure, not data.&lt;/p&gt;
&lt;p&gt;Think of it as plumbing — not water.&lt;/p&gt;
&lt;h3&gt;CREATE TABLE&lt;/h3&gt;
&lt;p&gt;Defines structure and constraints.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE orders (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  total NUMERIC(10,2) DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ALTER TABLE&lt;/h3&gt;
&lt;p&gt;Adds or modifies columns safely.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER TABLE orders
ADD COLUMN promo_code TEXT;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DROP TABLE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;DROP TABLE orders;
&lt;/code&gt;&lt;/pre&gt;
&lt;…</content:encoded></item><item><title>Polymorphic Decorators in React: HOCs on Steroids</title><link>https://jsdev.space/polymorphic-hocs-ts/</link><guid isPermaLink="true">https://jsdev.space/polymorphic-hocs-ts/</guid><description>A deep dive into type-safe polymorphic decorators (HOCs) in React with clean logic composition, reusable routing, and advanced TypeScript patterns.</description><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern React applications rarely fail because of rendering complexity.
They fail because logic spreads across components in unpredictable ways.&lt;/p&gt;
&lt;p&gt;Analytics. Routing. Permissions. Feature flags. Loading states. A/B
testing. Logging.&lt;/p&gt;
&lt;p&gt;All of these are cross-cutting concerns. When they are implemented
directly inside JSX trees, components become deeply nested, hard to
maintain, and almost impossible to scale in large teams.&lt;/p&gt;
&lt;p&gt;This article explores a production-grade architectural approach:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Polymorphic decorators implemented as strongly typed Higher-Order
Components (HOCs).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The objectives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Separate logic from rendering&lt;/li&gt;
&lt;li&gt;Preserve strict TypeScript safety&lt;/li&gt;
&lt;li&gt;Enable reusable composition&lt;/li&gt;
&lt;li&gt;Avoid JSX nesting hell&lt;/li&gt;
&lt;li&gt;Scale across large React applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not theory. Everything Here is practical and
production-oriented.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Core Problem: JSX Nesting Explosion&lt;/h2&gt;
&lt;p&gt;Imagine a simple requirement.&lt;/p&gt;
&lt;p&gt;A button must:&lt;/p&gt;
&lt;p&gt;. Generate an href from &lt;code&gt;/product/:id&lt;/code&gt;
. Inject route parameters
. Send analytics on click
. Stay polymorphic
. Remain fully type-safe&lt;/p&gt;
&lt;p&gt;The typical JSX composition quickly becomes unreadable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TrackClick
  as={(props) =&amp;gt; (
    &amp;lt;ResolveRoute
      as={PrimaryButton}
      template=&quot;/product/:id&quot;
      params={{ id: &quot;42&quot; }}
      {...props}
    /&amp;gt;
  )}
  elementKey=&quot;hero-btn&quot;
  eventLabel=&quot;hero_click&quot;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or using asChild-style composition:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ResolveRoute template=&quot;/product/:id&quot; params={{ id: &quot;42&quot; }} asChild&amp;gt;
  &amp;lt;TrackClick elementKey=&quot;hero-btn&quot; eventLabel=&quot;hero_click&quot; asChild&amp;gt;
    &amp;lt;PrimaryButton variant=&quot;solid&quot;&amp;gt;
      Buy Now
    &amp;lt;/PrimaryButton&amp;gt;
  &amp;lt;/TrackClick&amp;gt;
&amp;lt;/ResolveRoute&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though it works, readability suffers.&lt;/p&gt;
&lt;p&gt;Now compare with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const SmartButton = withRouteResolver(
  withAnalyticsTracking(PrimaryButton)
)

&amp;lt;SmartButton
  template=&quot;/product/:id&quot;
  params={{ id: &quot;42&quot; }}
  elementKey=&quot;hero-btn&quot;
  eventLabel=&quot;hero_click&quot;
  variant=&quot;solid&quot;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flat. Predictable. Maintainable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Turning Polymorphic Components into Decorators&lt;/h2&gt;
&lt;p&gt;Classic polymorphic component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {
  ElementType,
  createElement,
  MouseEventHandler,
} from &quot;react&quot;

type MergeProps&amp;lt;Base, Extra&amp;gt; =
  Omit&amp;lt;Base, keyof Extra&amp;gt; &amp;amp; Extra

type PropsOf&amp;lt;T extends ElementType&amp;gt; =
  React.ComponentPropsWithoutRef&amp;lt;T&amp;gt;

function WithSideEffect&amp;lt;
  TComponent extends ElementType&amp;lt;{ onClick?: MouseEventHandler }&amp;gt;
&amp;gt;({
  as,
  onClick,
  sideEffect,
  ...rest
}: MergeProps&amp;lt;
  PropsOf&amp;lt;TComponent&amp;gt;,
  {
    as: TComponent
    sideEffect?: MouseEventHandler
    onClick?: MouseEventHandler
  }
&amp;gt;) {
  const handleClick: MouseEventHandler = (event) =&amp;gt; {
    onClick?.(event)
    sideEffect?.(event)
  }

  return createElement(as, {
    ...rest,
    onClick: handleClick,
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The transformation is simple:&lt;/p&gt;
&lt;p&gt;Remove &lt;code&gt;as&lt;/code&gt; from props and accept the component as a function parameter.&lt;/p&gt;
&lt;p&gt;That converts polymorphic behavior into a reusable decorator.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Analytics Decorator (Production-Ready)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { ComponentType, MouseEventHandler } from &quot;react&quot;

type AnalyticsProps = {
  elementKey: string
  eventLabel?: string
}

export function withAnalyticsTracking&amp;lt;
  TBase extends {
    onClick?: MouseEventHandler
    label?: string
  }
&amp;gt;(BaseComponent: ComponentType&amp;lt;TBase&amp;gt;) {
  return function AnalyticsWrapped(
    props: TBase &amp;amp; AnalyticsProps
  ) {
    const {
      elementKey,
      eventLabel,
      onClick,
      ...rest
    } = props

    const handleClick: MouseEventHandler = (event) =&amp;gt; {
      console.log(&quot;Tracking event:&quot;, {
        elementKey,
        label: eventLabel ?? props.label ?? elementKey,
      })

      onClick?.(event)
    }

    return (
      &amp;lt;BaseComponent
        {...(rest as TBase)}
        onClick={handleClick}
      /&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This decorator:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preserves original props&lt;/li&gt;
&lt;li&gt;Injects analytics&lt;/li&gt;
&lt;li&gt;Keeps type inference intact&lt;/li&gt;
&lt;li&gt;Works with any compatible component&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Route Resolver Decorator&lt;/h2&gt;
&lt;p&gt;Centralized routing logic prevents chaos.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type RouteProps&amp;lt;T extends string&amp;gt; = {
  template: T
  params: Record&amp;lt;string, string&amp;gt;
}

export function withRouteResolver&amp;lt;
  TBase extends { href?: string }
&amp;gt;(BaseComponent: ComponentType&amp;lt;TBase&amp;gt;) {
  return function RouteWrapped&amp;lt;
    TTemplate extends string
  &amp;gt;(props: TBase &amp;amp; RouteProps&amp;lt;TTemplate&amp;gt;) {
    const { template, params, ...rest } = props

    const resolvedHref = Object.entries(params).reduce(
      (url, [key, value]) =&amp;gt;
        url.replace(`:${key}`, value),
      template
    )

    return (
      &amp;lt;BaseComponent
        {...(rest as TBase)}
        href={resolvedHref}
      /&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Template validation&lt;/li&gt;
&lt;li&gt;Central formatting&lt;/li&gt;
&lt;li&gt;Localization integration&lt;/li&gt;
&lt;li&gt;Analytics chaining&lt;/li&gt;
&lt;li&gt;Strict parameter control&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Composing Decorators Safely&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const EnhancedButton = withRouteResolver(
  withAnalyticsTracking(PrimaryButton)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Composition remains predictable because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each decorator isolates logic&lt;/li&gt;
&lt;li&gt;Rendering remains untouched&lt;/li&gt;
&lt;li&gt;TypeScript enforces compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Building a Decoratable Card System&lt;/h2&gt;
&lt;p&gt;Base card:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type CardBaseProps = {
  className?: string
  style?: React.CSSProperties
  children?: React.ReactNode
}

export function CardBase({
  className,
  style,
  children,
}: CardBaseProps) {
  return (
    &amp;lt;div
      className={`rounded-lg p-4 shadow ${className ?? &quot;&quot;}`}
      style={style}
    &amp;gt;
      {children}
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Loading decorator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function withLoadingState&amp;lt;
  T extends { children?: React.ReactNode }
&amp;gt;(BaseComponent: ComponentType&amp;lt;T&amp;gt;) {
  return function LoadingWrapped(
    props: T &amp;amp; { isLoading?: boolean }
  ) {
    const { isLoading, children, ...rest } = props

    return (
      &amp;lt;BaseComponent {...(rest as T)}&amp;gt;
        {isLoading ? &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt; : children}
      &amp;lt;/BaseComponent&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compose utility:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function compose(
  ...decorators: Array&amp;lt;
    (component: ComponentType&amp;lt;any&amp;gt;) =&amp;gt; ComponentType&amp;lt;any&amp;gt;
  &amp;gt;
) {
  return (base: ComponentType&amp;lt;any&amp;gt;) =&amp;gt;
    decorators.reduceRight(
      (acc, decorator) =&amp;gt; decorator(acc),
      base
    )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Final card:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const Card = compose(
  withLoadingState
)(CardBase)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;TypeScript Limitation: Generic Collisions&lt;/h2&gt;
&lt;p&gt;Decorators work perfectly --- until multiple introduce computed
generics.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return function WithRoute&amp;lt;TTemplate extends string&amp;gt;(
  props: ExtractProps&amp;lt;TBase&amp;gt; &amp;amp; LinkProps&amp;lt;TTemplate&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stacking several generic-heavy decorators may:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collapse inference&lt;/li&gt;
&lt;li&gt;Override template literal types&lt;/li&gt;
&lt;li&gt;Reduce safety guarantees&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Practical rule:&lt;/p&gt;
&lt;p&gt;Use only one computed-generic decorator per chain.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where This Pattern Excels&lt;/h2&gt;
&lt;p&gt;Ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Permission layers&lt;/li&gt;
&lt;li&gt;Feature flags&lt;/li&gt;
&lt;li&gt;Centralized routing&lt;/li&gt;
&lt;li&gt;Business rule injection&lt;/li&gt;
&lt;li&gt;UI library extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Less ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Highly dynamic render patterns (FACC is stronger there)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Architectural Impact&lt;/h2&gt;
&lt;p&gt;In large React + TypeScript applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Components are not always under your cont…</content:encoded></item><item><title>Nexus State: A Modern Atomic State Manager for JavaScript</title><link>https://jsdev.space/nexus-state-manager/</link><guid isPermaLink="true">https://jsdev.space/nexus-state-manager/</guid><description>Deep technical overview of Nexus State, a minimal atomic state manager with async atoms, middleware, persistence, and built-in DevTools.</description><pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;State management remains one of the most complex areas in frontend engineering. Over the years, we’ve seen Redux, MobX, Zustand, and Jotai attempt to balance structure, performance, and developer experience.&lt;/p&gt;
&lt;p&gt;Nexus State introduces an atomic model built around simplicity while still offering powerful capabilities such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in DevTools&lt;/li&gt;
&lt;li&gt;Time Travel debugging&lt;/li&gt;
&lt;li&gt;Async state primitives&lt;/li&gt;
&lt;li&gt;Persistence plugins&lt;/li&gt;
&lt;li&gt;Middleware system&lt;/li&gt;
&lt;li&gt;Multi-framework support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article explores its architecture, core patterns, and practical usage.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Core Concept: Atoms&lt;/h2&gt;
&lt;p&gt;Atoms are minimal, isolated pieces of state.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { atom, createStore } from &apos;@nexus-state/core&apos;

const sessionCounter = atom(0, &apos;sessionCounter&apos;)

const multipliedValue = atom((read) =&amp;gt; read(sessionCounter) * 5, &apos;multipliedValue&apos;)

const mainStore = createStore()

mainStore.set(sessionCounter, 4)
console.log(mainStore.get(multipliedValue)) // 20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Derived atoms recompute automatically when dependencies change.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Store &amp;amp; Subscriptions&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const unsubscribeHandler = mainStore.subscribe(sessionCounter, (value) =&amp;gt; {
  console.log(&apos;Updated:&apos;, value)
})

mainStore.set(sessionCounter, 12)
unsubscribeHandler()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The store handles dependency tracking and batched updates.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { useAtom } from &apos;@nexus-state/react&apos;

const progressAtom = atom(0, &apos;progress&apos;)
const percentAtom = atom((read) =&amp;gt; read(progressAtom) * 10, &apos;percent&apos;)

export function ProgressPanel() {
  const [progress, updateProgress] = useAtom(progressAtom)
  const [percent] = useAtom(percentAtom)

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;Progress: {progress}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Percentage: {percent}%&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; updateProgress(prev =&amp;gt; prev + 1)}&amp;gt;
        Increase
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only components depending on changed atoms re-render.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Async Atoms&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { asyncAtom } from &apos;@nexus-state/async&apos;

const [accountAtom, fetchAccount] = asyncAtom({
  fetchFn: async () =&amp;gt; {
    const response = await fetch(&apos;/api/account&apos;)
    return response.json()
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Async atoms encapsulate loading, error, and resolved states.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Persistence Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { persist, localStorageStorage } from &apos;@nexus-state/persist&apos;

persist(progressAtom, {
  key: &apos;app_progress&apos;,
  storage: localStorageStorage
})(mainStore)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;State is automatically restored on reload.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Middleware Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { middleware } from &apos;@nexus-state/middleware&apos;

const auditMiddleware = middleware(progressAtom, {
  beforeSet: (_, next) =&amp;gt; {
    console.log(&apos;Before:&apos;, next)
    return next
  },
  afterSet: (_, next) =&amp;gt; {
    console.log(&apos;After:&apos;, next)
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Performance Characteristics&lt;/h2&gt;
&lt;p&gt;Nexus State optimizes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Selective component updates&lt;/li&gt;
&lt;li&gt;Batched state mutations&lt;/li&gt;
&lt;li&gt;Lazy evaluation of derived atoms&lt;/li&gt;
&lt;li&gt;Zero DevTools overhead in production builds&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Comparison Snapshot&lt;/h2&gt;
&lt;p&gt;Compared to Redux:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Less boilerplate&lt;/li&gt;
&lt;li&gt;No action types&lt;/li&gt;
&lt;li&gt;Native async support&lt;/li&gt;
&lt;li&gt;Smaller bundle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to Zustand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in Time Travel&lt;/li&gt;
&lt;li&gt;Multi-framework support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to MobX:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicit dependency graph&lt;/li&gt;
&lt;li&gt;Smaller runtime size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to Jotai:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store instance isolation&lt;/li&gt;
&lt;li&gt;Built-in persistence&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;When Nexus State Makes Sense&lt;/h2&gt;
&lt;p&gt;Use it when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want atomic isolation&lt;/li&gt;
&lt;li&gt;You need computed state out of the box&lt;/li&gt;
&lt;li&gt;Debugging tools matter&lt;/li&gt;
&lt;li&gt;You work across multiple frameworks&lt;/li&gt;
&lt;li&gt;You build micro-frontends&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;p&gt;Documentation: &lt;a href=&quot;https://nexus-state.website.yandexcloud.net&quot;&gt;nexus-state.website.yandexcloud.net&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Source Code: &lt;a href=&quot;https://sourcecraft.dev/astashkin-a/nexus-state&quot;&gt;sourcecraft.dev/astashkin-a/nexus-state&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Demo Applications: &lt;a href=&quot;https://codesandbox.io/p/sandbox/nryqsj&quot;&gt;codesandbox.io/p/sandbox/nryqsj&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/algorithm-complexity-big-o/&quot;&gt;Understanding Big O Notation with JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/animejs-animation-guide/&quot;&gt;Mastering Web Animations with Anime.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/await-fetch-slow/&quot;&gt;Optimizing await fetch(): Why It Slows Down and How to Speed It Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>