JavaScript Development Space - Master JS and NodeJSExplore the world of JavaScript at our blog, your ultimate resource for guides, tutorials, and articles. Uncover the latest insights, tips, and trends.https://jsdev.space/en-usHowto Use IMask.js for Input Masking in JavaScript Formshttps://jsdev.space/howto/imaskjs-input-masking/https://jsdev.space/howto/imaskjs-input-masking/Learn how to implement input masking for cards, dates, currency, and more using IMask.js with real-world examples and best practices.Tue, 17 Mar 2026 00:00:00 GMT<h2>Introduction</h2>
<p>Form handling is one of the most underestimated parts of frontend development.</p>
<p>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.</p>
<p>Input masking solves this at the source.</p>
<p>Instead of reacting to bad input after submission, you guide the user while they type.</p>
<p>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.</p>
<hr />
<h2>Why Input Masking Is a Core UX Layer</h2>
<p>Without masking:</p>
<ul>
<li>users guess formats</li>
<li>validation fails often</li>
<li>error messages increase friction</li>
<li>backend receives inconsistent data</li>
</ul>
<p>With masking:</p>
<ul>
<li>input is guided instantly</li>
<li>errors are prevented early</li>
<li>formatting becomes predictable</li>
<li>UX feels faster and more professional</li>
</ul>
<p>Masking is not just visual formatting — it's a <strong>data normalization layer at the UI level</strong>.</p>
<hr />
<h2>What Makes IMask.js Different</h2>
<p>IMask.js stands out because it combines flexibility with performance.</p>
<p>Key characteristics:</p>
<ul>
<li>no dependencies</li>
<li>small bundle (~15kb gzipped)</li>
<li>works with plain JS and frameworks</li>
<li>supports multiple mask types</li>
<li>allows custom logic via functions</li>
<li>handles mobile input well</li>
</ul>
<p>Unlike many libraries, IMask doesn’t lock you into one approach. It gives you primitives to build your own behavior.</p>
<hr />
<h2>Installation</h2>
<pre><code>pnpm add imask
</code></pre>
<pre><code>import MaskCore from "imask"
</code></pre>
<hr />
<h2>Mental Model of IMask</h2>
<p>Before writing code, understand how IMask works internally.</p>
<p>There are three key concepts:</p>
<ol>
<li><strong>Input Element</strong> – the DOM node</li>
<li><strong>Mask Configuration</strong> – rules defining formatting</li>
<li><strong>Mask Instance</strong> – runtime controller</li>
</ol>
<pre><code>const inputNode = document.querySelector("#phone")
const maskController = MaskCore(inputNode, {
mask: "+{1} (000) 000-0000"
})
</code></pre>
<p>The instance acts as a bridge between raw input and formatted output.</p>
<hr />
<h2>Basic Example: Phone Input</h2>
<pre><code><input id="contact-phone" placeholder="+1 (___) ___-____" />
</code></pre>
<pre><code>const phoneNode = document.querySelector("#contact-phone")
const phoneController = MaskCore(phoneNode, {
mask: "+{1} (000) 000-0000"
})
</code></pre>
<p>As the user types, formatting is applied automatically.</p>
<hr />
<h2>Static Masks (Fixed Structure)</h2>
<p>Use static masks when format is strictly defined.</p>
<pre><code>const cardNode = document.querySelector("#card")
const cardController = MaskCore(cardNode, {
mask: "0000 0000 0000 0000"
})
</code></pre>
<p>Best for:</p>
<ul>
<li>credit cards</li>
<li>IDs</li>
<li>formatted codes</li>
</ul>
<hr />
<h2>Dynamic Masks (Multiple Formats)</h2>
<pre><code>const phoneDynamic = MaskCore(document.querySelector("#phone"), {
mask: [
{ mask: "+{1} (000) 000-0000" },
{ mask: "+{44} 0000 000000" }
]
})
</code></pre>
<p>IMask automatically selects the correct pattern based on input.</p>
<hr />
<h2>Number Mask (Currency & Prices)</h2>
<pre><code>const amountNode = document.querySelector("#amount")
const currencyController = MaskCore(amountNode, {
mask: Number,
min: 0,
max: 1000000,
thousandsSeparator: ",",
radix: ".",
precision: 2,
prefix: "$ "
})
</code></pre>
<p>This handles:</p>
<ul>
<li>formatting</li>
<li>decimal precision</li>
<li>limits</li>
<li>separators</li>
</ul>
<hr />
<h2>Date Mask</h2>
<pre><code>const dateNode = document.querySelector("#date")
const dateController = MaskCore(dateNode, {
mask: Date,
pattern: "yyyy-mm-dd",
lazy: false
})
</code></pre>
<p>This prevents invalid formats like <code>2026-99-99</code>.</p>
<hr />
<h2>Regex Mask</h2>
<pre><code>const emailNode = document.querySelector("#email")
const emailController = MaskCore(emailNode, {
mask: /^\S+@\S+\.\S+$/
})
</code></pre>
<p>Good for:</p>
<ul>
<li>simple validation rules</li>
<li>constrained input</li>
</ul>
<hr />
<h2>Function Mask (Full Control)</h2>
<pre><code>const evenNode = document.querySelector("#even")
const evenController = MaskCore(evenNode, {
mask: (value) => {
const parsed = Number(value)
return parsed % 2 === 0 ? value : value.slice(0, -1)
}
})
</code></pre>
<p>This approach is powerful but should be used carefully.</p>
<hr />
<h2>Real-World Example: Credit Card Detection</h2>
<pre><code>const ccNode = document.querySelector("#cc")
const ccController = MaskCore(ccNode, {
mask: "0000 0000 0000 0000"
})
ccController.on("accept", () => {
const raw = ccController.unmaskedValue
let type = "unknown"
if (/^4/.test(raw)) type = "visa"
else if (/^5[1-5]/.test(raw)) type = "mastercard"
else if (/^3[47]/.test(raw)) type = "amex"
console.log(type)
})
</code></pre>
<hr />
<h2>Events System</h2>
<p>IMask exposes lifecycle hooks:</p>
<pre><code>maskController.on("accept", () => {
console.log(maskController.value)
})
maskController.on("complete", () => {
console.log("complete")
})
maskController.on("reject", (input) => {
console.log("rejected", input)
})
</code></pre>
<p>These allow integration with UI state, validation, and analytics.</p>
<hr />
<h2>Advanced Configuration</h2>
<pre><code>MaskCore(inputNode, {
mask: "0000-00-00",
lazy: true,
nullable: false,
unmask: true,
placeholderChar: "_",
prepare: (val) => val.toUpperCase(),
validate: (val) => val.length === 10
})
</code></pre>
<p>Important options:</p>
<ul>
<li><code>lazy</code> → allow partial input</li>
<li><code>unmask</code> → return raw value</li>
<li><code>prepare</code> → transform input</li>
<li><code>validate</code> → enforce rules</li>
</ul>
<hr />
<h2>React Integration</h2>
<pre><code>import { useEffect, useRef } from "react"
import MaskCore from "imask"
export function MaskedInput() {
const nodeRef = useRef(null)
useEffect(() => {
const instance = MaskCore(nodeRef.current, {
mask: "+{1} (000) 000-0000"
})
return () => instance.destroy()
}, [])
return <input ref={nodeRef} />
}
</code></pre>
<hr />
<h2>Common Pitfalls</h2>
<h3>1. Changing Value Directly</h3>
<p>Wrong:</p>
<pre><code>inputNode.value = "123"
</code></pre>
<p>Correct:</p>
<pre><code>maskController.value = "123"
</code></pre>
<hr />
<h3>2. Autofill Conflicts</h3>
<pre><code><input autocomplete="off" />
</code></pre>
<p>Or sync manually:</p>
<pre><code>inputNode.addEventListener("change", () => {
maskController.updateValue()
})
</code></pre>
<hr />
<h3>3. Mobile Input Issues</h3>
<p>Use:</p>
<pre><code>lazy: true
</code></pre>
<p>to reduce input jitter.</p>
<hr />
<h2>Best Practices</h2>
<ul>
<li>keep masks readable</li>
<li>avoid over-engineering regex</li>
<li>always use unmaskedValue for backend</li>
<li>keep commits atomic (if integrating with forms logic)</li>
<li>test on mobile devices</li>
</ul>
<hr />
<h2>When NOT to Use Masking</h2>
<p>Avoid masking when:</p>
<ul>
<li>input is free text</li>
<li>validation is business-driven (not format-driven)</li>
<li>accessibility would suffer</li>
</ul>
<hr />
<h2>Conclusion</h2>
<p>IMask.js is not just a utility — it's a UX upgrade.</p>
<p>It allows you to:</p>
<ul>
<li>enforce structure at input level</li>
<li>reduce validation complexity</li>
<li>improve perceived performance</li>
<li>create predictable user flows</li>
</ul>
<p>Once integrated properly, your forms stop being fragile.</p>
<p>They become reliable systems.</p>
<p>And in modern frontend development, that’s a huge advantage.</p…Git for Beginners: Branches, Commits, and Your First Pull Requesthttps://jsdev.space/git-branches-commits-pull-request/https://jsdev.space/git-branches-commits-pull-request/A practical beginner’s guide to Git workflows. Learn how branches, commits, and pull requests work together in real development teams.Mon, 16 Mar 2026 00:00:00 GMT<p>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.</p>
<p>Yet for beginners, Git often feels intimidating.</p>
<p>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.</p>
<p>And the worst part? Many developers don’t realize this is a problem.</p>
<p>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:</p>
<pre><code>fix
update
changes
fix again
</code></pre>
<p>Good luck.</p>
<p>Git is not just a storage system for code.
It is a communication tool between developers.</p>
<p>In this guide, you'll learn how Git actually works and how to use it in a way that makes collaboration smooth and professional.</p>
<h2>Git Is Not Magic — It’s Just a Graph</h2>
<p>Before learning commands, it's important to understand what Git really stores.</p>
<p>Git keeps project history as a graph of commits.</p>
<p>Each commit represents a snapshot of the project at a specific moment in time.</p>
<p>A commit contains:</p>
<ul>
<li>file changes</li>
<li>author information</li>
<li>timestamp</li>
<li>a unique hash</li>
<li>a commit message</li>
</ul>
<p>A branch is simply a pointer to a commit.</p>
<p>When you create a new commit, the branch pointer moves forward.</p>
<p>A simplified commit graph might look like this:</p>
<pre><code>A --- B --- C (main)
\
D --- E (feature/auth)
</code></pre>
<p><img src="./images/git-workflow.png" alt="Git commit graph" /></p>
<p>In this example:</p>
<ul>
<li><code>main</code> contains the stable project history</li>
<li><code>feature/auth</code> is a separate branch for developing a new feature</li>
<li>once the feature is complete, it gets merged back into <code>main</code></li>
</ul>
<p>This isolation is what allows teams to work safely without interfering with each other.</p>
<h2>Types of Git Branches</h2>
<p>For small projects, teams often only use one main branch and a few temporary branches.</p>
<p>But larger projects usually follow a structured workflow. One popular example is Git Flow.</p>
<p>Git Flow defines several types of branches.</p>
<h3>Main branches</h3>
<p><strong>main</strong></p>
<p>The production-ready branch.
Everything here should be stable and deployable.</p>
<p><strong>develop</strong></p>
<p>The integration branch where new features are combined before release.</p>
<h3>Temporary branches</h3>
<p><strong>feature/</strong>*</p>
<p>Branches used to develop new functionality.</p>
<p>Example:</p>
<pre><code>feature/user-authentication
</code></pre>
<p><strong>release/</strong>*</p>
<p>Used to prepare a new release.</p>
<p>Tasks may include:</p>
<ul>
<li>bug fixes</li>
<li>documentation updates</li>
<li>version bumps</li>
</ul>
<p><strong>hotfix/</strong>*</p>
<p>Urgent fixes applied directly to production code.</p>
<p>Example:</p>
<pre><code>hotfix/security-patch
</code></pre>
<h2>A simpler workflow for beginners</h2>
<p>Most teams teaching Git to newcomers use a simpler model:</p>
<pre><code>main
├─ feature/add-login
├─ feature/create-api
└─ fix/email-validation
</code></pre>
<p>Each task gets its own branch.</p>
<p>When the task is done, the branch is merged and deleted.</p>
<h2>Creating Your First Branch</h2>
<pre><code>git clone https://github.com/example/project.git
</code></pre>
<p>Move into the project directory:</p>
<pre><code>cd project
</code></pre>
<p>Make sure your main branch is up to date:</p>
<pre><code>git switch main
git pull origin main
</code></pre>
<p>Now create a new branch for your task:</p>
<pre><code>git switch -c feature/add-user-model
</code></pre>
<p>This command:</p>
<ol>
<li>creates a new branch</li>
<li>switches to it immediately</li>
</ol>
<p>Older Git versions used:</p>
<pre><code>git checkout -b feature/add-user-model
</code></pre>
<p>Both commands do the same thing.</p>
<h2>Naming Branches Correctly</h2>
<p>Branch naming conventions matter more than beginners think.</p>
<p>Bad example:</p>
<pre><code>branch1
new-feature
test
</code></pre>
<p>Good examples:</p>
<pre><code>feature/add-user-service
bugfix/login-error
hotfix/token-expiration
</code></pre>
<p>General rules:</p>
<ul>
<li>use lowercase</li>
<li>avoid spaces</li>
<li>use hyphens or slashes</li>
<li>avoid non-ASCII characters</li>
</ul>
<p>Clear names help the entire team understand what the branch contains.</p>
<h2>Making Meaningful Commits</h2>
<p>After writing some code, the next step is creating commits.</p>
<p>Imagine we add a simple JavaScript class:</p>
<pre><code>class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
</code></pre>
<p>Before committing, Git needs to know which files to include.</p>
<p>Add the file to the staging area:</p>
<pre><code>git add src/main/project/User.js
</code></pre>
<p>You can also add all changes:</p>
<pre><code>git add .
</code></pre>
<p>But this is risky — temporary files might accidentally be committed.</p>
<p>It is usually safer to add files explicitly.</p>
<h2>Creating the commit</h2>
<pre><code>git commit -m "Add User class"
</code></pre>
<p>This works, but the message could be better.</p>
<p>A good commit message answers two questions:</p>
<ol>
<li>What changed?</li>
<li>Why did it change?</li>
</ol>
<h2>Writing Good Commit Messages</h2>
<p>A commonly used structure is:</p>
<pre><code>Short summary (max ~50 characters)
Detailed explanation of why the change was needed.
Explain important technical details if necessary.
</code></pre>
<p>Example:</p>
<pre><code>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.
</code></pre>
<p>Good commit messages help future developers understand the reasoning behind code changes.</p>
<p>And often that future developer is you six months later.</p>
<h2>Conventional Commits</h2>
<p>Many teams use the Conventional Commits standard.</p>
<p>It adds a prefix describing the type of change.</p>
<p>Examples:</p>
<pre><code>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
</code></pre>
<p>Common prefixes:</p>
<table>
<thead>
<tr>
<th>Prefix</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>feat</td>
<td>new feature</td>
</tr>
<tr>
<td>fix</td>
<td>bug fix</td>
</tr>
<tr>
<td>docs</td>
<td>documentation changes</td>
</tr>
<tr>
<td>style</td>
<td>formatting only</td>
</tr>
<tr>
<td>refactor</td>
<td>code improvements without behavior change</td>
</tr>
<tr>
<td>test</td>
<td>new or updated tests</td>
</tr>
<tr>
<td>chore</td>
<td>tooling or configuration changes</td>
</tr>
</tbody>
</table>
<p>Example commit history:</p>
<pre><code>feat: add User entity
fix: validate email format
refactor: extract email validation utility
test: add negative login tests
</code></pre>
<p>This makes the project history easy to understand.</p>
<p>It also enables tools that automatically generate changelogs.</p>
<h2>Sending Code to GitHub</h2>
<p>Once your work is committed, push it to the remote repository.</p>
<pre><code>git push origin feature/add-user-model
</code></pre>
<p>If the branch doesn't exist on the server yet, Git will create it.</p>
<p>After pushing, go to GitHub (or GitLab) and you'll see a button:</p>
<p><strong>Create Pull Request</strong></p>
<h2>What Is a Pull Request?</h2>
<p>A Pull Request (PR) is a request to merge your changes into another branch.</p>
<p>Usually:</p>
<pre><code>feature branch → main
</code></pre>
<p>Pull requests allow teammates to:</p>
<ul>
<li>review code</li>
<li>suggest improvements</li>
<li>detect bugs early</li>
</ul>
<p>This …Meet Effect TS: A New Way to Structure TypeScript Appshttps://jsdev.space/meet-effect-ts/https://jsdev.space/meet-effect-ts/Discover Effect TS, a powerful TypeScript framework for typed errors, dependency injection, structured concurrency, and composable backend architecture.Fri, 13 Mar 2026 00:00:00 GMT<p>The JavaScript ecosystem evolves extremely fast. New frameworks appear
constantly, each promising better performance, better developer
experience, or a simpler programming model.</p>
<p><a href="https://effect.website/">Effect TS</a> is different.</p>
<p>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: <strong>how complex TypeScript applications should be
structured</strong>.</p>
<p>Most real-world applications suffer from the same problems:</p>
<ul>
<li>asynchronous code becomes messy</li>
<li>errors are unpredictable</li>
<li>dependency injection becomes fragile</li>
<li>background tasks are hard to manage</li>
<li>libraries do not compose well together</li>
</ul>
<p>Effect TS approaches these problems from a new angle. Instead of solving
them separately, it introduces a unified programming model where
<strong>errors, dependencies, concurrency, and resources are all part of the
type system</strong>.</p>
<p>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.</p>
<p>This article introduces the core ideas behind Effect and explains why
more developers are experimenting with it in their TypeScript projects.</p>
<hr />
<h2>Why Developers Are Trying Effect</h2>
<p>Effect has been gaining attention because it combines several powerful
ideas into a single framework.</p>
<p>Below are ten practical reasons why developers start exploring it.</p>
<h2>1. Errors Become Part of the Type System</h2>
<p>Traditional JavaScript error handling is chaotic. A function can throw
anything: an Error instance, a string, or even an arbitrary object.</p>
<p>In many projects this leads to code where the real failure scenarios are
not clearly defined.</p>
<p>Effect changes this by making errors explicit.</p>
<p>Instead of writing:</p>
<pre><code>async function getUser(id: string): Promise<User>
</code></pre>
<p>you describe both success and failure:</p>
<pre><code>Effect<User, UserNotFoundError | NetworkError>
</code></pre>
<p>Now the compiler knows exactly which errors are possible. When those
errors are handled later, the type system verifies that no cases were
forgotten.</p>
<p>This makes applications far more predictable.</p>
<hr />
<h2>2. Dependency Injection Without Magic</h2>
<p>Many backend frameworks use dependency injection containers based on
decorators or runtime reflection.</p>
<p>While these systems work, they also hide dependencies behind runtime
behavior.</p>
<p>Effect takes a different approach.</p>
<p>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.</p>
<p>This eliminates an entire category of runtime errors.</p>
<hr />
<h2>3. Testing Becomes Much Simpler</h2>
<p>Because dependencies are explicit, testing becomes easier.</p>
<p>Instead of mocking modules globally or configuring complicated
containers, you can simply replace a service implementation with a test
version.</p>
<p>This leads to tests that are:</p>
<ul>
<li>easier to understand</li>
<li>easier to maintain</li>
<li>less dependent on internal implementation details</li>
</ul>
<p>For large systems this improvement alone can be extremely valuable.</p>
<hr />
<h2>4. Everything Is Designed to Compose</h2>
<p>Effect encourages a compositional style of programming.</p>
<p>Instead of deeply nested async code, logic is built as small pieces that
combine through pipelines.</p>
<p>For example:</p>
<pre><code>pipe(
fetchUsers(),
Effect.map(filterActiveUsers),
Effect.tap(logUserCount),
Effect.flatMap(saveUsers)
)
</code></pre>
<p>Each step transforms the previous result.</p>
<p>This approach keeps complex workflows readable and easy to refactor.</p>
<hr />
<h2>5. Gradual Adoption Is Possible</h2>
<p>One of the biggest advantages of Effect is that it does not require
rewriting an entire application.</p>
<p>Existing Promise-based code can be wrapped using helper utilities.
Effects can also be executed as regular Promises.</p>
<p>This means teams can adopt Effect incrementally. A project might start
with a few critical services and expand from there.</p>
<hr />
<h2>6. Concurrency Is Built Into the Model</h2>
<p>JavaScript applications often require complex asynchronous workflows.</p>
<p>Typical solutions involve combining:</p>
<ul>
<li>Promise.all</li>
<li>retry libraries</li>
<li>AbortController</li>
<li>custom scheduling logic</li>
</ul>
<p>Effect replaces this with a unified concurrency model.</p>
<p>The framework provides tools for:</p>
<ul>
<li>parallel execution</li>
<li>task cancellation</li>
<li>retries with backoff</li>
<li>scheduling</li>
<li>background tasks</li>
</ul>
<p>All using the same abstractions.</p>
<hr />
<h2>7. A Rich Built-In Toolkit</h2>
<p>Effect is not just a runtime. It also includes a large ecosystem of
utilities.</p>
<p>Examples include:</p>
<ul>
<li>Streams</li>
<li>Queues</li>
<li>PubSub channels</li>
<li>Schedulers</li>
<li>Date/time helpers</li>
<li>transactional memory</li>
<li>schema validation</li>
</ul>
<p>Instead of installing many unrelated libraries, developers often rely on
the tools provided by Effect itself.</p>
<p>This reduces fragmentation in large codebases.</p>
<hr />
<h2>8. Observability Is Built In</h2>
<p>Modern applications require visibility into their behavior.</p>
<p>Logging, metrics, and tracing are essential for debugging and monitoring
production systems.</p>
<p>Effect integrates observability directly into the framework and supports
standards such as OpenTelemetry.</p>
<p>This makes it easier to instrument applications without complex
integrations.</p>
<hr />
<h2>9. A Growing Ecosystem</h2>
<p>The core Effect team maintains several official packages that extend the
framework.</p>
<p>Some examples include:</p>
<ul>
<li><strong>@effect/platform</strong> -- platform utilities like HTTP and filesystem</li>
<li><strong>@effect/sql</strong> -- typed database access</li>
<li><strong>@effect/cli</strong> -- command‑line application framework</li>
<li><strong>@effect/rpc</strong> -- typed remote procedure calls</li>
<li><strong>@effect/cluster</strong> -- primitives for distributed systems</li>
<li><strong>@effect/ai</strong> -- abstractions for language model integrations</li>
</ul>
<p>Because these packages follow the same design philosophy, they integrate
smoothly with each other.</p>
<hr />
<h2>10. Strong Type Feedback Helps AI Tools</h2>
<p>AI‑assisted development tools rely heavily on compiler feedback.</p>
<p>Effect's strong typing provides extremely clear signals when generated
code is incorrect.</p>
<p>This often leads to faster corrections and fewer runtime bugs when using
AI coding assistants.</p>
<hr />
<h2>The Core Idea: Effects</h2>
<p>At the heart of the framework lies a single abstraction:</p>
<pre><code>Effect<A, E, R>
</code></pre>
<p>This type represents a computation.</p>
<p>The three parameters describe:</p>
<p><strong>A</strong> --- the successful result<br />
<strong>E</strong> --- the error type<br />
<strong>R</strong> --- required dependencies</p>
<p>Compared to a Promise, which only represents success, Effect provides a
complete description of a computation.</p>
<p>This allows the compiler to reason about behavior that would otherwise
only appear at runtime.</p>
<hr />
<h2>Creating Effects</h2>
<p>The framework provides several helpers for constructing effects.</p>
<p>For example:</p>
<pre><code>const value = Effect.succeed(42)
</code></pre>
<p>Creating a failure:</p>
<pre><code>const error = Effect.fail("Unexpected error")
</code></pre>
<p>Wrapping synchronous logic:</p>
<pre><code>const random = Effect.sync(() => Math.random())
</code></pre>
<p>These operations describe work rather than executing it immediatel…Howto Deploy OpenClaw and Build Your Personal AI Second Brainhttps://jsdev.space/howto/deploy-openclaw-second-brain/https://jsdev.space/howto/deploy-openclaw-second-brain/Learn how to deploy OpenClaw from scratch on Windows, macOS, or Linux and build a private AI assistant with memory, tool integrations, and automation.Fri, 13 Mar 2026 00:00:00 GMT<p>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. <a href="https://openclaw.ai/">OpenClaw</a> takes a different approach.</p>
<p>Instead of functioning purely as a chatbot, OpenClaw acts as a
<strong>programmable cognitive partner</strong>. It can:</p>
<ul>
<li>remember long‑term context</li>
<li>connect to external tools</li>
<li>automate workflows</li>
<li>run entirely on infrastructure you control</li>
</ul>
<p>This guide explains how to deploy OpenClaw from scratch and configure it
on <strong>Windows, macOS, or Linux</strong>.</p>
<p>By the end of this tutorial you will have a working OpenClaw instance
running locally.</p>
<hr />
<h2>Why Use OpenClaw</h2>
<h3>Private Deployment</h3>
<p>All conversations and memory stay on your own machine or server.</p>
<h3>Tool Integration</h3>
<p>OpenClaw can interact with tools such as:</p>
<ul>
<li>GitHub</li>
<li>calendars</li>
<li>APIs</li>
<li>automation workflows</li>
</ul>
<h3>Persistent Memory</h3>
<p>OpenClaw includes both <strong>short‑term and long‑term memory</strong>, allowing it
to remember:</p>
<ul>
<li>user preferences</li>
<li>project details</li>
<li>conversation context</li>
</ul>
<h3>Open Source</h3>
<p>OpenClaw is released under the MIT license, meaning the entire system is
fully auditable and customizable.</p>
<hr />
<h2>System Requirements</h2>
<p>Minimum environment:</p>
<pre><code>Node.js 18+
npm or pnpm
8 GB RAM recommended
2 GB disk space
</code></pre>
<p>Supported platforms:</p>
<ul>
<li>macOS</li>
<li>Linux</li>
<li>Windows (PowerShell or WSL2)</li>
</ul>
<hr />
<h2>Verify Your Environment</h2>
<h3>macOS / Linux</h3>
<pre><code>node --version
npm --version
</code></pre>
<p>If Node is missing, install it from:</p>
<p>https://nodejs.org</p>
<hr />
<h2>Installation Method 1: One‑Step Installer</h2>
<h3>macOS / Linux</h3>
<p><code>curl -fsSL https://docs.openclaw.ai/install.sh | bash</code></p>
<p>After installation open:</p>
<p><code>http://localhost:18789</code></p>
<h3>Windows (PowerShell)</h3>
<p>Clone the repository and start the gateway manually:</p>
<pre><code>git clone https://github.com/openclawai/openclaw.git
cd openclaw
npm install
npm run start:gateway
</code></pre>
<h3>Windows with WSL2 (Recommended)</h3>
<p>Install WSL:</p>
<p><code>wsl --install</code></p>
<p>Then inside WSL:</p>
<p><code>curl -fsSL https://docs.openclaw.ai/install.sh | bash</code></p>
<p>Access the interface via:</p>
<p><code>http://localhost:18789</code></p>
<hr />
<h2>Installation Method 2: Manual Installation</h2>
<h3>Clone Repository</h3>
<pre><code>git clone https://github.com/openclawai/openclaw.git
cd openclaw
</code></pre>
<h3>Install Dependencies</h3>
<pre><code>npm install -g pnpm
pnpm install
</code></pre>
<p>or</p>
<p><code>npm install</code></p>
<h3>Configure Workspace</h3>
<p>macOS / Linux</p>
<pre><code>export OPENCLAW_WORKSPACE=~/.openclaw/workspace
mkdir -p $OPENCLAW_WORKSPACE
</code></pre>
<p>Windows PowerShell</p>
<pre><code>setx OPENCLAW_WORKSPACE "%USERPROFILE%\.openclaw\workspace"
mkdir %USERPROFILE%\.openclaw\workspace
</code></pre>
<h3>Copy Configuration</h3>
<p><code>cp config.example.yaml config.yaml</code></p>
<p>Windows</p>
<p><code>copy config.example.yaml config.yaml</code></p>
<h3>Start Gateway</h3>
<p><code>pnpm start:gateway</code></p>
<hr />
<h2>Docker Deployment</h2>
<p>Create <code>docker-compose.yml</code></p>
<pre><code> version: "3.8"
services:
openclaw:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw
ports:
- "18789:18789"
- "18792:18792"
environment:
- OPENCLAW_WORKSPACE=/workspace
- NODE_ENV=production
volumes:
- ./workspace:/workspace
- ./config.yaml:/app/config.yaml
- ./logs:/var/log/openclaw
restart: unless-stopped
</code></pre>
<p>Start the container:</p>
<p><code>docker-compose up -d</code></p>
<p>View logs:</p>
<p><code>docker-compose logs -f</code></p>
<hr />
<h2>Configuring AI Models</h2>
<p>Example configuration in <code>config.yaml</code>:</p>
<pre><code> agents:
defaults:
model: "siliconflow/deepseek-ai/DeepSeek-V3.2"
</code></pre>
<p>Alternative models:</p>
<pre><code>openai/gpt-4
anthropic/claude-3-opus
google/gemini-2.0
</code></pre>
<h2>Enabling Skills</h2>
<p>Skills allow OpenClaw to interact with external services.</p>
<p>Example configuration:</p>
<pre><code> skills:
enabled:
- github
- weather
</code></pre>
<p>Install additional skills:</p>
<p><code>openclaw skills install github</code></p>
<hr />
<h2>Creating Your First Custom Skill</h2>
<p>Create directory:</p>
<p><code>mkdir ~/.openclaw/workspace/skills/my-skill</code></p>
<p>Create <code>SKILL.md</code>:</p>
<pre><code># Custom Skill
## Description
Automation skill for internal APIs.
## Usage
/my-skill [arguments]
## Capabilities
- API access
- file reading
- workflow automation
</code></pre>
<hr />
<h1>Troubleshooting</h1>
<h3>Port Already In Use</h3>
<p>macOS / Linux</p>
<p><code>lsof -i :18789</code></p>
<p>Windows</p>
<p><code>netstat -ano | findstr 18789</code></p>
<p>Kill process:</p>
<p>Linux/macOS</p>
<p><code>pkill -f openclaw</code></p>
<p>Windows</p>
<p><code>taskkill /PID <pid> /F</code></p>
<hr />
<h2>Conclusion</h2>
<p>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.</p>
<p>Once deployed, it can become a powerful <strong>personal AI infrastructure
layer</strong> that helps automate tasks, analyze code, and manage complex
workflows.</p>
<p>For developers interested in building their own AI ecosystem, OpenClaw
provides a strong foundation for creating a true <strong>digital second
brain</strong>.</p>
Friday Links #36 — JavaScript Ecosystem Weeklyhttps://jsdev.space/friday/friday-36/https://jsdev.space/friday/friday-36/A curated roundup of JavaScript ecosystem news: TypeScript, Node.js, Deno, AI developer tools, security discoveries, and new libraries from the past two weeks.Fri, 13 Mar 2026 00:00:00 GMT<p><img src="./images/friday-36.png" alt="Friday Links #36" /></p>
<p>The JavaScript ecosystem never slows down.<br />
Over the past two weeks we’ve seen <strong>major updates across runtimes, frameworks, tooling, and security</strong>.</p>
<p>In this issue of <strong>Friday Links</strong>, we highlight the most interesting developments across the JavaScript world — from infrastructure changes and new tools to security research and ecosystem trends.</p>
<h2>🧠 Ecosystem Highlights</h2>
<h3>TypeScript 6 Prepares the Path to TS7</h3>
<p>The TypeScript team <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-rc/">released</a> an early preview of <strong>TypeScript 6</strong>.</p>
<p>This release is mainly about <strong>internal changes preparing for the future Go-based compiler planned for TypeScript 7</strong>.</p>
<p>Key goals:</p>
<ul>
<li>faster compilation</li>
<li>reduced memory usage</li>
<li>better incremental builds</li>
<li>improved large project performance</li>
</ul>
<p>Large monorepos could see <strong>dramatic speed improvements</strong> once the Go compiler lands.</p>
<h3>Deno 2.7 Improves Node Compatibility</h3>
<p>The latest <a href="https://deno.com/blog/v2.7"><strong>Deno runtime release</strong></a> continues improving Node compatibility.</p>
<p>Highlights:</p>
<ul>
<li>improved npm integration</li>
<li>Node API compatibility</li>
<li>Temporal API stabilization</li>
</ul>
<p>Example:</p>
<pre><code>const now = Temporal.Now.instant()
console.log(now.toString())
</code></pre>
<hr />
<h2>📜 Articles & Tutorials</h2>
<p><a href="https://github.blog/ai-and-ml/generative-ai/under-the-hood-security-architecture-of-github-agentic-workflows/">Under the hood: Security architecture of GitHub Agentic Workflows</a></p>
<p><a href="https://hackernoon.com/beating-javascript-performance-limits-with-rust-and-n-api-building-a-faster-image-diff-tool">Beating JavaScript Performance Limits With Rust and N-API: Building a Faster Image Diff Tool</a></p>
<p><a href="https://css-tricks.com/the-different-ways-to-select-html-in-css/">The Different Ways to Select <code><html></code> in CSS</a></p>
<p><a href="https://frontendmasters.com/blog/the-big-gotcha-of-anchor-positioning/">The Big Gotcha of Anchor Positioning</a></p>
<p><a href="https://neciudan.dev/cline-ci-got-compromised-here-is-how">How to steal npm publish tokens by opening GitHub issues</a></p>
<p><a href="https://cardog.app/blog/vin-decoder-javascript">How to Decode a VIN in JavaScript</a></p>
<p><a href="https://blog.scottlogic.com/2026/03/09/noJS-3-flappy-bird.html">Making a Flappy Bird clone using pure HTML and CSS, no JavaScript</a></p>
<p><a href="https://www.ishchhabra.com/writing/pnpm-monorepo">How to build a pnpm monorepo, the right way</a></p>
<p><a href="https://www.mux.com/blog/react-is-changing-the-game-for-streaming-apps-with-the-activity-component">React is changing the game for streaming apps with the Activity component</a></p>
<p><a href="https://patrickbrosset.com/articles/2026-03-09-using-css-animations-as-state-machines-to-remember-focus-and-hover-states-with-css-only/">Using CSS animations as state machines to remember focus and hover states with CSS only</a></p>
<p><a href="https://blog.frankmtaylor.com/2026/03/05/you-dont-know-html-tables/">You Don’t Know HTML Tables</a></p>
<p><a href="https://reactdevelopment.substack.com/p/5-react-hooks-techniques-to-improve">5 React Hooks Techniques to Improve Component Performance</a></p>
<h2>⚒️ Tools</h2>
<h3>Repomix — Turn Any Repo Into a Single AI-Readable File</h3>
<p><img src="./images/repomix.png" alt="Repomix" /></p>
<p><a href="https://github.com/yamadashy/repomix">Repomix</a> packs an entire repository into a single AI-friendly document.</p>
<p><a href="https://github.com/tb5z035i/cursor-tg">Cursor Cloud Telegram Connector</a></p>
<p><a href="https://npmx.dev/">npmx</a> is an experimental tool designed to improve npm package exploration.</p>
<p><a href="https://litepacks.github.io/welyjs/">Wely</a> — Lightweight Web Component Framework</p>
<p><a href="https://github.com/vadimdemedes/ink">Ink</a> allows developers to build CLI tools using React components.</p>
<p><a href="https://sojinantony01.github.io/react-cron-generator/">Cron Expression Generator</a></p>
<h2>📚 Libs</h2>
<p><a href="https://github.com/vercel/nft">Node File Trace</a> - determines exactly which files a Node application needs to run.</p>
<p><a href="https://github.com/privatenumber/minification-benchmarks">JavaScript Minification Benchmarks</a>: SWC Still Leads</p>
<p><a href="https://rv-grid.com/">RevoGrid</a> - High-Performance Data Grid Component</p>
<p><a href="https://github.com/cosmiciron/vmprint">VMPrint</a> - A pure-JS, tiny typesetting engine with bit-perfect PDF output on everything—from Cloudflare Workers to the browser.</p>
<p><a href="https://github.com/quantizor/markdown-to-jsx">markdown-to-jsx</a> - A very fast and versatile markdown toolchain. Output to AST, React, React Native, SolidJS, Vue, HTML, and more!</p>
<p><a href="https://github.com/sindresorhus/clipboardy">clipboardy</a> - Access the system clipboard (copy/paste)</p>
<h2>⌚ Releases</h2>
<p><a href="https://github.com/solidjs/solid/releases/tag/v2.0.0-beta.0">Solid v2.0.0 Beta: The <code><Suspense></code> Era Comes to an End</a></p>
<p>After a long experimental phase, Solid 2.0 has released its first beta, introducing native asynchronous reactivity as a core feature of the framework.</p>
<p>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.</p>
<p>One notable change is that <code><Suspense></code> has been retired. For initial renders, it is now replaced by a simpler component called <code><Loading></code>.</p>
<p><a href="https://astro.build/blog/astro-6/">Astro 6 is here!</a></p>
<p><a href="https://nodejs.org/en/blog/release/v25.8.0">Node.js 25.8.0 (Current)</a></p>
<p><a href="https://eslint.org/blog/2026/03/eslint-v10.0.3-released/">ESLint v10.0.3 released</a></p>
<p><a href="https://blog.emberjs.com/ember-released-6-11/">Ember 6.11 Released</a></p>
<p><a href="https://ionic.io/blog/announcing-ionic-framework-8-8">Ionic Framework 8.8</a></p>
<p><a href="https://github.com/facebook/react-native/releases/tag/v0.85.0-rc.0">React Native 0.85 RC.0</a>, <a href="https://github.com/pnpm/pnpm/releases/tag/v10.32.0">pnpm 10.32</a>, <a href="https://github.com/jestjs/jest/releases/tag/v30.3.0">Jest 30.3</a>, <a href="https://github.com/recharts/recharts/releases/tag/v3.8.0">Recharts 3.8</a>,
<a href="https://github.com/openplayerjs/openplayerjs/releases/tag/v3.0.2">OpenPlayer.js 3.0.2</a>, <a href="https://github.com/prisma/prisma/releases/tag/7.5.0">Prisma 7.5</a>, <a href="https://github.com/sqliteai/sqlite-js">SQLite JS 1.3</a>, <a href="https://github.com/staylor/react-helmet-async/pull/260">React Helmet Async 3.0</a>, <a href="https://github.com/preactjs/preact/releases/tag/10.29.0">Preact 10.29.0</a></p>
<h2>📺 Videos</h2>
<p><a href="https://www.youtube.com/watch?v=IBTx5aGj-6U">Build Your Own Video Sharing App – Loom Clone with Next.js and Mux JavaScript Tutorial</a></p>
<p><a href="https://www.youtube.com/watch?v=_TRV6fPUMJw">You Can Just Ship Agents: Architecting for the Agentic Era | Dom Sipowicz, Vercel</a></p>
<p><a href="https://www.youtube.com/watch?v=wvt5JNUXXLM">The Future of TypeScript</a></p>
<p><a href="https://www.youtube.com/watch?v=IBTx5aGj-6U">Build Your Own Video Sharing App – Loom Clone with Next.js and Mux JavaScript Tutorial</a></p>
<p><a href="https://www.youtube.com/watch?v=abbeIUOCzmw">Cloudflare just slop forked Next.js…</a></p>
<p><a href="https://www.youtube.com/watch?v=Xn-gtHDsaPY">7 new open source AI tools you need right now…</a></p>
<p><a href="https://www.…Valibot vs Zod: A Lightweight Validation Alternativehttps://jsdev.space/valibot-vs-zod/https://jsdev.space/valibot-vs-zod/Compare Valibot and Zod in JavaScript and TypeScript apps. Explore bundle size, performance benchmarks, and real validation examples.Tue, 10 Mar 2026 00:00:00 GMT<h2>Valibot vs Zod — Can a 1KB Validator Replace Zod?</h2>
<p>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.</p>
<p>But in practice, external data <strong>cannot be trusted</strong>.</p>
<p>Fields disappear. Types change. Backends evolve. A simple typo in a property name can break an entire page.</p>
<p>Schema validation libraries solve this problem by adding a <strong>runtime validation layer</strong> between your application and external data.</p>
<p>Two libraries dominate this space today:</p>
<ul>
<li><a href="https://zod.dev/">Zod</a></li>
<li><a href="https://valibot.dev/">Valibot</a></li>
</ul>
<p>Zod has been the standard for years. Valibot, however, is a newer library designed with a different philosophy: <strong>smaller bundles and faster validation</strong>.</p>
<hr />
<h2>Why Validation Libraries Matter</h2>
<p>TypeScript provides compile‑time guarantees, but it cannot guarantee runtime correctness.</p>
<p>Example:</p>
<pre><code>const response = await fetch("/api/user/1")
const user = await response.json()
</code></pre>
<p>Runtime data might not match expectations.</p>
<p>Validation libraries ensure data integrity before it reaches business logic.</p>
<hr />
<h2>Validating API Responses with Valibot</h2>
<pre><code>import * as v from "valibot"
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<typeof ProductSchema>
export async function loadProduct(id:number):Promise<Product>{
const res = await fetch(`https://dummyjson.com/products/${id}`)
const data = await res.json()
try{
return v.parse(ProductSchema,data)
}
catch(error){
console.error("Invalid API response",error)
throw new Error("Product validation failed")
}
}
</code></pre>
<hr />
<h2>Validating Form Input</h2>
<pre><code>import * as v from "valibot"
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<typeof RegistrationSchema>
</code></pre>
<hr />
<h2>Cross‑Field Validation</h2>
<pre><code>const RegistrationSchema = v.pipe(
v.object({
email:v.pipe(
v.string("Email must be text"),
v.email("Enter a valid email")
),
age:v.pipe(
v.string(),
v.digits("Age must contain digits"),
v.transform(Number),
v.minValue(18,"You must be at least 18")
),
password:v.pipe(
v.string(),
v.minLength(6,"Password must contain at least 6 characters")
),
confirmPassword:v.string()
}),
v.forward(
v.partialCheck(
[["password"],["confirmPassword"]],
({password,confirmPassword}) => password === confirmPassword,
"Passwords do not match"
),
["confirmPassword"]
)
)
</code></pre>
<hr />
<h2>Code Comparison</h2>
<h3>Valibot</h3>
<pre><code>import * as v from "valibot"
const BookSchema = v.object({
title:v.string(),
price:v.number()
})
v.parse(BookSchema,{title:"JavaScript Guide",price:30})
</code></pre>
<h3>Zod</h3>
<pre><code>import {z} from "zod"
const BookSchema = z.object({
title:z.string(),
price:z.number()
})
BookSchema.parse({title:"JavaScript Guide",price:30})
</code></pre>
<hr />
<h2>Benchmark Scripts</h2>
<h3>Valibot</h3>
<pre><code>import * as v from "valibot"
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:"Laptop",
price:1500,
rating:4.8,
tags:["tech","computer"]
}
function benchmarkValibot(iterations:number){
const start = performance.now()
for(let i=0;i<iterations;i++){
v.parse(schema,sample)
}
return performance.now() - start
}
console.log("Valibot:",benchmarkValibot(10000),"ms")
</code></pre>
<h3>Zod</h3>
<pre><code>import {z} from "zod"
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:"Laptop",
price:1500,
rating:4.8,
tags:["tech","computer"]
}
function benchmarkZod(iterations:number){
const start = performance.now()
for(let i=0;i<iterations;i++){
schema.parse(sample)
}
return performance.now() - start
}
console.log("Zod:",benchmarkZod(10000),"ms")
</code></pre>
<hr />
<h2>Benchmark Results</h2>
<h3>Valid data</h3>
<table>
<thead>
<tr>
<th>Library</th>
<th>min (ms)</th>
<th>max (ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Valibot</td>
<td>26.58</td>
<td>27.31</td>
</tr>
<tr>
<td>Zod</td>
<td>67.02</td>
<td>70.25</td>
</tr>
</tbody>
</table>
<h3>Invalid data</h3>
<table>
<thead>
<tr>
<th>Library</th>
<th>min (ms)</th>
<th>max (ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Valibot</td>
<td>48.63</td>
<td>54.70</td>
</tr>
<tr>
<td>Zod</td>
<td>143.63</td>
<td>147.79</td>
</tr>
</tbody>
</table>
<hr />
<h2>Bundle Size</h2>
<p>Replacing Zod with Valibot reduced bundle size by <strong>~11.4 KB (gzip)</strong> in a medium project.</p>
<hr />
<h2>Popularity</h2>
<table>
<thead>
<tr>
<th>Library</th>
<th>Weekly downloads</th>
</tr>
</thead>
<tbody>
<tr>
<td>Valibot</td>
<td>~1.2M</td>
</tr>
<tr>
<td>Zod</td>
<td>~89.5M</td>
</tr>
</tbody>
</table>
<hr />
<h2>Conclusion</h2>
<p>Validation libraries protect applications from unreliable external data.</p>
<p>Valibot proves that validation can be <strong>fast, lightweight, and developer‑friendly</strong>.</p>
<p>For new projects, Valibot is definitely worth considering.</p>
Why Blindly Using JSON.parse() Can Be Dangeroushttps://jsdev.space/safe-json-parse-javascript/https://jsdev.space/safe-json-parse-javascript/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.Fri, 06 Mar 2026 00:00:00 GMT<p>If you write JavaScript for long enough—whether in the browser or on a Node.js server—you’ll almost certainly use <code>JSON.parse()</code> countless times.</p>
<p>It’s one of the most common APIs in the ecosystem.</p>
<p>Developers rely on it for everything from reading configuration files to handling API requests:</p>
<pre><code>const user = JSON.parse(localStorage.getItem("user"))
const payload = JSON.parse(req.body.payload)
const config = JSON.parse(fs.readFileSync("config.json", "utf-8"))
</code></pre>
<p>It’s simple, fast, and built directly into the language.</p>
<p>But here’s the problem:</p>
<blockquote>
<p>Many developers treat <code>JSON.parse()</code> as a harmless utility, when in reality it can become a security risk if used carelessly.</p>
</blockquote>
<p>The function itself isn’t dangerous—but blindly parsing untrusted input can expose your application to attacks such as:</p>
<ul>
<li>Prototype pollution</li>
<li>Denial of Service (DoS)</li>
<li>Data injection attacks</li>
<li>Unexpected runtime crashes</li>
</ul>
<p>In this article, we’ll explore why this happens and <strong>how to implement secure JSON parsing strategies in modern JavaScript applications</strong>.</p>
<h2>Understanding What JSON.parse() Actually Does</h2>
<p><code>JSON.parse()</code> converts a JSON string into a JavaScript object.</p>
<p>Example:</p>
<pre><code>const json = '{"name": "Alice", "role": "admin"}'
const user = JSON.parse(json)
console.log(user.name)
// Alice
</code></pre>
<p>The function faithfully reconstructs the <strong>entire object structure</strong> contained in the JSON string.</p>
<p>That includes every property name provided by the input.</p>
<p>And that’s where the problems start.</p>
<h2>Security Risk #1: Prototype Pollution</h2>
<p>One of the most common vulnerabilities related to unsafe JSON handling is prototype pollution.</p>
<p>Prototype pollution occurs when attackers manipulate an object's prototype to inject properties into every object in the application.</p>
<p>If your code merges or copies parsed objects without validation, an attacker can inject special keys like:</p>
<pre><code>__proto__
constructor
prototype
</code></pre>
<p>These keys can modify global object behavior.</p>
<h3>Example of a Prototype Pollution Payload</h3>
<pre><code>const payload = '{"__proto__": {"isAdmin": true}}'
</code></pre>
<p>Now imagine parsing it and merging the object into your system:</p>
<pre><code>const data = JSON.parse(payload)
Object.assign({}, data)
console.log({}.isAdmin)
// true
</code></pre>
<p>Suddenly every object in your application has a new property.</p>
<p>This can lead to severe consequences:</p>
<ul>
<li>Authentication bypass</li>
<li>Privilege escalation</li>
<li>Corrupted application logic</li>
<li>Security policy violations</li>
</ul>
<p>Many well-known vulnerabilities in JavaScript ecosystems have involved prototype pollution through unvalidated input.</p>
<h3>Why This Happens</h3>
<p>JavaScript objects inherit from <code>Object.prototype</code>.</p>
<p>If attackers manage to inject properties into the prototype chain, the impact becomes global.</p>
<p>For example:</p>
<pre><code>console.log({}.toString)
</code></pre>
<p>This works because every object inherits from <code>Object.prototype</code>.</p>
<p>If attackers add properties there, every object in your system changes behavior.</p>
<h2>Security Risk #2: Denial of Service (DoS)</h2>
<p>Another common attack vector is resource exhaustion.</p>
<p>Attackers can send extremely large or deeply nested JSON strings that consume huge amounts of CPU or memory during parsing.</p>
<h3>Example: Deep Nesting Attack</h3>
<pre><code>const evil = '{"a":{"a":{"a":{"a":{"a":{"a":{}}}}}}}'
</code></pre>
<p>Even small nested objects can cause heavy recursion.</p>
<p>With enough depth, parsing can freeze the event loop.</p>
<h3>Example: Huge Array Attack</h3>
<pre><code>const payload = `[${"1,".repeat(10000000)}1]`
</code></pre>
<p>A JSON string representing ten million elements can:</p>
<ul>
<li>Allocate gigabytes of memory</li>
<li>Freeze Node.js for seconds</li>
<li>Crash the process with an Out Of Memory error</li>
</ul>
<p>For public APIs, this effectively becomes a remote kill switch.</p>
<h2>Secure JSON Parsing Strategy</h2>
<p>The safest approach combines three defensive layers:</p>
<ol>
<li>Input size limits</li>
<li>Key filtering</li>
<li>Schema validation</li>
</ol>
<p>Let’s look at each one.</p>
<h3>Step 1: Limit Input Size</h3>
<p>Before parsing JSON, always check the size of the input.</p>
<pre><code>function safeParse(jsonString, maxSize = 100 * 1024) {
if (typeof jsonString !== "string") {
throw new Error("Invalid input type")
}
if (jsonString.length > maxSize) {
throw new Error("JSON payload too large")
}
return JSON.parse(jsonString)
}
</code></pre>
<p>This prevents attackers from sending extremely large payloads.</p>
<p>In production APIs, limits are often set between <strong>50KB and 1MB</strong>, depending on use case.</p>
<h3>Step 2: Block Dangerous Keys</h3>
<p>JavaScript allows a reviver function when parsing JSON.</p>
<p>This lets you inspect every property during parsing.</p>
<p>You can use it to reject suspicious keys.</p>
<pre><code>function secureParse(jsonString) {
return JSON.parse(jsonString, (key, value) => {
if (key === "__proto__" || key === "constructor" || key === "prototype") {
throw new Error("Forbidden key detected")
}
return value
})
}
</code></pre>
<p>This simple check prevents prototype pollution payloads.</p>
<h3>Step 3 (Best Practice): Runtime Schema Validation</h3>
<p>Modern JavaScript applications rarely trust raw data.</p>
<p>Instead, they validate data structures using schema validation libraries like:</p>
<ul>
<li>Zod</li>
<li>Joi</li>
<li>Yup</li>
<li>Ajv</li>
</ul>
<p>These tools ensure the parsed data matches an expected structure.</p>
<h3>Example Using Zod</h3>
<pre><code>import { z } from "zod"
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)
}
</code></pre>
<p>Benefits:</p>
<ul>
<li>Automatic runtime validation</li>
<li>Clear error messages</li>
<li>Type inference for TypeScript</li>
<li>Protection against unexpected fields</li>
</ul>
<p>Schema validation is considered modern best practice in production systems.</p>
<h2>Additional Risks in Node.js</h2>
<p>Server environments are especially vulnerable because JSON often comes from external sources.</p>
<p>Common risky inputs include:</p>
<h3>HTTP Requests</h3>
<pre><code>req.body
</code></pre>
<h3>Uploaded Configuration Files</h3>
<p>Users may upload JSON configuration files.</p>
<p>If parsed blindly, they can trigger crashes.</p>
<h3>Database Data</h3>
<p>Stored JSON may contain unexpected fields or corrupted structures.</p>
<h3>Third-Party Webhooks</h3>
<p>External services send JSON payloads that should never be trusted blindly.</p>
<h2>Recommended Defensive Checklist</h2>
<p>Before parsing JSON in production systems, verify:</p>
<ul>
<li>The data source is trusted</li>
<li>The payload size is limited</li>
<li>Dangerous keys are filtered</li>
<li>The structure matches a schema</li>
</ul>
<p>This layered approach dramatically reduces attack surfaces.</p>
<h2>Is <code>JSON.parse()</code> Actually Unsafe?</h2>
<p>Not exactly.</p>
<p><code>JSON.parse()</code> itself does not execute JavaScript code.</p>
<p>Unlike <code>eval()</code>, it only reconstructs objects.</p>
<p>However:</p>
<blockquote>
<p>Using it without validating input is equivalent to trusting user data blindly.</p>
</blockquote>
<p>And trusting user input is one of the most common sources of security vulnerabilities.</p>
<h2>Final Thoughts</h2>
<p><code>JSON.parse()</code> is not a bad API.</p>
<p>But using it without safeguards is like handling a powerful tool without protective equipm…Tailwind CSS v4 vs MUI, Ant Design & Styled Componentshttps://jsdev.space/tailwind-v4-vs-mui-antd-styled-components/https://jsdev.space/tailwind-v4-vs-mui-antd-styled-components/Architectural comparison of Tailwind CSS v4, MUI, Ant Design, and Styled Components—runtime costs, design tokens, dead CSS, theming, and scaling tradeoffs.Fri, 06 Mar 2026 00:00:00 GMT<p>In 2026, picking a styling approach is less about taste and more about architecture.</p>
<p>Teams don’t just “choose CSS.” They choose:</p>
<ul>
<li>how design decisions are encoded and shared,</li>
<li>how fast UI can change without regressions,</li>
<li>how much runtime work happens on every render,</li>
<li>how quickly a codebase accumulates styling debt,</li>
<li>and how portable the solution is across frameworks and products.</li>
</ul>
<p>This article compares four popular directions:</p>
<ul>
<li><a href="https://tailwindcss.com/">Tailwind CSS v4</a> (utility-first styling engine)</li>
<li><a href="https://mui.com/">MUI</a> and <a href="https://ant.design/">Ant Design</a> (component libraries with strong opinions)</li>
<li><a href="https://styled-components.com/">Styled Components</a> (CSS-in-JS)</li>
</ul>
<p>The goal isn’t to crown a universal winner. It’s to help you pick the right tool <em>for your system’s constraints</em>.</p>
<h2>1. These tools are different “layers” of the stack</h2>
<p>The biggest mistake is to compare them as if they are interchangeable.</p>
<h3>MUI and Ant Design: component systems (not just styling)</h3>
<p><img src="./images/mui-ant-design.png" alt="MUI and Ant Design" /></p>
<p>MUI and Ant Design are ready-made UI component ecosystems. You’re not just getting colors and spacing. You’re getting:</p>
<ul>
<li>behavior,</li>
<li>accessibility (A11Y) defaults,</li>
<li>keyboard navigation,</li>
<li>focus management in modals,</li>
<li>edge-case handling that took years to harden.</li>
</ul>
<p>If you need to ship an internal admin tool quickly, this matters.</p>
<p>But you pay for it in other ways:</p>
<ul>
<li>They impose a <strong>DOM structure</strong> (wrappers, internal elements, slots).</li>
<li>They impose an <strong>opinionated design language</strong> (Material for MUI, Ant’s system for Ant).</li>
<li>Customization often becomes an ongoing negotiation between your design system and theirs.</li>
</ul>
<p>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.</p>
<h3>Tailwind v4: styling engine (not a component library)</h3>
<p><img src="./images/tailwind.png" alt="Tailwind v4" /></p>
<p>Tailwind doesn’t know what a Date Picker is. It doesn’t ship one. It gives you low-level primitives to build your own.</p>
<p>Tailwind’s advantage is control:</p>
<ul>
<li>you decide markup structure,</li>
<li>you decide constraints,</li>
<li>you can match a custom design system without constantly overriding component internals.</li>
</ul>
<p>Tailwind v4 also reduced setup friction: you can start with a single CSS import (<code>@import "tailwindcss";</code>) instead of the older directive-heavy setup.</p>
<h3>Styled Components: component-local styling with runtime generation</h3>
<p>Styled Components sits between the two extremes:</p>
<ul>
<li>not a component library,</li>
<li>but tightly binds CSS to components.</li>
</ul>
<p>This is productive—until the project grows and you start asking:</p>
<ul>
<li>Which styles are still used?</li>
<li>Which are dead?</li>
<li>What’s the real cost of style generation during renders?</li>
<li>How do we maintain consistent tokens without duplicating logic across many components?</li>
</ul>
<p>Tailwind and CSS-in-JS solve different problems. If you treat them as the same category, you’ll make architectural tradeoffs accidentall</p>
<h2>2. Performance: static CSS vs runtime style generation</h2>
<p>Performance isn’t just about “Tailwind is fast.” It’s about where work happens.</p>
<h3>Runtime overhead (especially visible at scale)</h3>
<p>CSS-in-JS solutions and some UI libraries generate and manage styles at runtime. That means:</p>
<ul>
<li>extra JavaScript execution,</li>
<li>extra style insertion/management,</li>
<li>more work during render-heavy scenarios (large tables, infinite lists, fast state updates).</li>
</ul>
<p>Tailwind’s output is <strong>static CSS</strong>. The browser receives it once and applies it like normal styles.</p>
<p>That’s not automatically “always faster,” but it changes the performance profile in a way that becomes very noticeable in high-frequency UI updates.</p>
<h3>Build performance in Tailwind v4</h3>
<p>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.</p>
<p>That matters in real teams because build speed directly affects:</p>
<ul>
<li>iteration loops,</li>
<li>design token tweaking,</li>
<li>refactoring velocity,</li>
<li>developer experience.</li>
</ul>
<h2>3. “Self-cleaning CSS”: why dead styles don’t stick around (as much)</h2>
<p>Large projects die by a thousand cuts—most of them are “small”:</p>
<ul>
<li>unused classes,</li>
<li>abandoned component variants,</li>
<li>theme overrides nobody remembers,</li>
<li>CSS specificity battles that accumulate over years.</li>
</ul>
<h3>The typical CSS-in-JS failure mode</h3>
<p>CSS-in-JS keeps styles “close” to components, which is great for local reasoning.</p>
<p>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.</p>
<p>Teams end up with a question they can’t easily answer:</p>
<blockquote>
<p>“Is this style still used anywhere?”</p>
</blockquote>
<h3>Tailwind’s approach: generate only what’s referenced</h3>
<p>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.</p>
<p>So when you delete a component, the classes that disappear from your markup will stop being included in the compiled CSS.</p>
<p>This is one reason Tailwind projects often avoid the classic “CSS landfill” problem.</p>
<h3>What about helpers like <code>tailwind-variants</code>?</h3>
<p>Utilities like <a href="https://www.tailwind-variants.org/"><code>tailwind-variants</code></a> (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.”</p>
<h2>4. Design tokens: from Figma to code without losing alignment</h2>
<p>Modern UI teams don’t want “colors in CSS.” They want tokens:</p>
<ul>
<li><code>--color-brand</code></li>
<li><code>--spacing-section</code></li>
<li><code>--radius-card</code></li>
<li><code>--font-sans</code></li>
</ul>
<h3>Tailwind v4’s CSS-first theming</h3>
<p>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.</p>
<p>Example:</p>
<pre><code>@import "tailwindcss";
@theme {
--color-brand: oklch(0.55 0.22 260);
--color-brand-hover: oklch(0.45 0.22 260);
--font-sans: "Inter", system-ui;
--spacing-section: 4rem;
}
</code></pre>
<p>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.</p>
<p>Then UI usage becomes simple and consistent:</p>
<pre><code>export function BrandButton() {
return (
<button className="bg-brand hover:bg-brand-hover text-white px-4 py-2 rounded">
Button
</button>
);
}
</code></pre>
<p>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.</p>
<h3>How this compares to MUI / Styled Components theming</h3>
<p>MUI theming is powerful, but it typically lives in JavaScript objects and flows through providers and library AP…JavaScript Note: ToggleEvent.source and dialog.closedByhttps://jsdev.space/toggleevent-source-dialog-closedby/https://jsdev.space/toggleevent-source-dialog-closedby/Practical guide to ToggleEvent.source and dialog.closedBy for cleaner dialog and popover behavior in modern browsers.Fri, 06 Mar 2026 00:00:00 GMT<p>The modern web platform continues evolving with small but powerful
improvements. Two recent additions --- <a href="https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent/source"><strong><code>ToggleEvent.source</code></strong></a> and the
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby"><strong><code>closedby</code> attribute for <code><dialog></code></strong></a> --- make working with dialogs
and popovers significantly easier.</p>
<p><code>ToggleEvent.source</code> allows developers to determine <strong>which element
triggered a popover or dialog visibility change</strong>, while <code>closedby</code>
allows you to <strong>declare how a dialog can be closed</strong> without writing
extra JavaScript.</p>
<p>These additions move the web platform further toward <strong>declarative UI
behavior</strong>.</p>
<hr />
<h2>ToggleEvent.source</h2>
<p>The <strong><code>source</code> property</strong> of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent"><code>ToggleEvent</code></a> interface is a
<strong>read‑only reference to the element that triggered the toggle event</strong>.</p>
<p>In simple terms, it tells you:</p>
<blockquote>
<p>Which element opened or closed a popover or dialog.</p>
</blockquote>
<p>The property returns an instance of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element"><strong><code>Element</code></strong></a>.</p>
<p>If the visibility change was triggered <strong>programmatically</strong>, the value
will be <strong><code>null</code></strong>.</p>
<h3>Browser support</h3>
<p>Most modern browsers <a href="https://caniuse.com/wf-toggleevent-source">already support</a> the property. Safari currently
exposes it behind an experimental flag.</p>
<p><img src="./images/ToggleEvent-source.png" alt="Browser support for ToggleEvent.source" /></p>
<h3>Elements that can trigger popovers</h3>
<p>A popover can be triggered by:</p>
<ul>
<li><code><button commandfor="..."></code> <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#commandfor">commandfor attribute</a></li>
<li><code><button popovertarget="..."></code> <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#popovertarget">popovertarget attribute</a></li>
<li><code><input type="button" popovertarget="..."></code></li>
</ul>
<h3>Elements that can behave as popovers</h3>
<ul>
<li><code><dialog></code></li>
<li>Any element with the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover"><code>popover</code></a> attribute</li>
</ul>
<hr />
<h2>Example</h2>
<p>Consider a dialog with multiple buttons that close it.</p>
<p>We want to determine <strong>which button closed the dialog</strong> and display the
result.</p>
<pre><code><div>
<button commandfor="my-dialog" command="show-modal">
Show modal dialog
</button>
<dialog id="my-dialog">
<h3>Do you like modern Web APIs?</h3>
<div style="display:flex; gap:10px">
<button commandfor="my-dialog" command="close" data-answer="yes">
Yes
</button>
<button commandfor="my-dialog" command="close" data-answer="sure">
Sure
</button>
</div>
</dialog>
<p>No answer yet</p>
</div>
</code></pre>
<p>Now we listen for the <code>toggle</code> event.</p>
<pre><code>const paragraph = document.querySelector("p");
document.querySelector("dialog").addEventListener("toggle", (event) => {
if (!(event.source instanceof HTMLButtonElement)) return;
const { answer } = event.source.dataset;
if (answer) {
paragraph.textContent = `Answer: ${answer}`;
}
});
</code></pre>
<p>With <code>ToggleEvent.source</code>, determining the trigger element becomes
trivial.</p>
<h2><Codepen id="019cbfd7-7afe-776d-afbe-461c3c60fb50" /></h2>
<h2>HTMLDialogElement.closedBy</h2>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/closedBy"><strong><code>closedby</code> attribute</strong></a> defines <strong>which user actions are allowed to
close a dialog</strong>.</p>
<p>Previously, developers often needed custom JavaScript logic to control
this behavior. Now it can be defined directly in HTML.</p>
<p>This attribute is supported by all major browsers (Safari currently ships it behind an experimental flag).</p>
<p><img src="./images/dialog-closedby.png" alt="Browser support for dialog.closedBy" /></p>
<h3>Supported closing actions</h3>
<p>Dialogs can be closed by:</p>
<ol>
<li>Clicking outside the dialog on the overlay (light dismiss).</li>
<li>Platform actions such as pressing <strong>Esc</strong>.</li>
<li>A developer-defined action such as a button calling
<a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close"><code>dialog.close()</code></a>.</li>
</ol>
<hr />
<h2>Attribute values</h2>
<h3>any</h3>
<p>The dialog can be closed using <strong>all methods</strong> above.</p>
<h3>closerequest</h3>
<p>The dialog can be closed by:</p>
<ul>
<li>pressing <strong>Esc</strong></li>
<li>developer-defined logic</li>
</ul>
<h3>none</h3>
<p>The dialog can only be closed <strong>programmatically or by explicit UI
controls</strong>.</p>
<hr />
<h2>Default behavior</h2>
<p>The default value depends on how the dialog is opened.</p>
<p>If opened using: <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal"><code>showModal()</code></a>, the default becomes: <code>closerequest</code>.</p>
<p>Otherwise the default is: <code>none</code></p>
<h3>Why this matters</h3>
<p>Before <code>closedby</code>, implementing overlay click closing required JavaScript logic, such as custom hooks (<a href="https://jsdev.space/10-custom-react-hooks/"><code>useClickOutside</code></a> in React).</p>
<p>Now this behavior can be defined purely declaratively.</p>
<hr />
<h2>Example with multiple dialogs</h2>
<pre><code><div class="demo">
<button class="open" commandfor="dlg-any" command="show-modal">
Open dialog (closedby="any")
</button>
<!-- 1) closedby="any" -->
<dialog id="dlg-any" closedby="any">
<div class="dialog">
<header class="header">
<h3>Dialog A — closedby="any"</h3>
<button class="icon" commandfor="dlg-any" command="close" aria-label="Close">
✖
</button>
</header>
<p>
This dialog can be closed by clicking the backdrop, pressing Esc, or using a Close button.
</p>
<footer class="footer">
<button class="cancel" commandfor="dlg-any" command="close">Close</button>
<button class="confirm" commandfor="dlg-closerequest" command="show-modal">
Open dialog B
</button>
</footer>
</div>
</dialog>
<!-- 2) closedby="closerequest" -->
<dialog id="dlg-closerequest" closedby="closerequest">
<div class="dialog">
<header class="header">
<h3>Dialog B — closedby="closerequest"</h3>
<button class="icon" commandfor="dlg-closerequest" command="close" aria-label="Close">
✖
</button>
</header>
<p>
This dialog closes via Esc or explicit controls. Clicking the backdrop should NOT close it.
</p>
<footer class="footer">
<button class="cancel" commandfor="dlg-closerequest" command="close">Close</button>
<button class="confirm" commandfor="dlg-none" command="show-modal">
Open dialog C
</button>
</footer>
</div>
</dialog>
<!-- 3) closedby="none" -->
<dialog id="dlg-none" closedby="none">
<div class="dialog">
<header cla…How to Build an LRU Cache from Scratch in JavaScripthttps://jsdev.space/howto/lru-cache-javascript/https://jsdev.space/howto/lru-cache-javascript/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.Fri, 06 Mar 2026 00:00:00 GMT<p>Caching is a fundamental optimization technique used across nearly every modern software system. Whether you're building a web API, a database engine, or a frontend application, caching can dramatically improve performance by storing frequently accessed data in memory.</p>
<p>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.</p>
<p>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.</p>
<p>In this guide, we’ll build an LRU cache from scratch, following a clear progression:</p>
<ol>
<li>Understanding what LRU actually means</li>
<li>Defining the required operations</li>
<li>Choosing the right data structures</li>
<li>Implementing the cache step by step</li>
<li>Testing the implementation</li>
<li>Avoiding common mistakes</li>
</ol>
<p>By the end, you'll have a production-ready LRU cache implementation in JavaScript.</p>
<h2>What Is an LRU Cache?</h2>
<p>LRU stands for <strong>Least Recently Used</strong>.</p>
<p>The idea is simple:</p>
<blockquote>
<p>When the cache reaches its capacity, remove the item that hasn't been used for the longest time.</p>
</blockquote>
<p>Every time an item is accessed or inserted, it becomes the <strong>most recently used</strong> entry.</p>
<h2>A Simple Real-World Analogy</h2>
<p>Imagine a small bookshelf that can only hold three books.</p>
<p>Capacity = 3</p>
<h3>Step 1</h3>
<pre><code>Add Algorithms
</code></pre>
<h3>Step 2</h3>
<p>Add Java</p>
<pre><code>[Algorithms, Java]
</code></pre>
<h3>Step 3</h3>
<p>Add Python</p>
<pre><code>[Algorithms, Java, Python]
</code></pre>
<p>The shelf is now full.</p>
<h3>Step 4 – Access "Algorithms"</h3>
<p>Because you used it recently, it moves to the end:</p>
<pre><code>[Java, Python, Algorithms]
</code></pre>
<h3>Step 5 – Add "JavaScript"</h3>
<p>Since the shelf is full, remove the least recently used item (<strong>Java</strong>):</p>
<pre><code>[Python, Algorithms, JavaScript]
</code></pre>
<p>This behavior is exactly how an <strong>LRU cache works</strong>.</p>
<h2>Core Requirements of an LRU Cache</h2>
<p>In most implementations (and interviews), the cache supports two operations.</p>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Description</th>
<th>Required Complexity</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>get(key)</code></td>
<td>Return the value associated with the key, or <code>-1</code> if not found. Also mark it as recently used.</td>
<td>O(1)</td>
</tr>
<tr>
<td><code>put(key, value)</code></td>
<td>Insert or update a value. If capacity is exceeded, remove the least recently used item.</td>
<td>O(1)</td>
</tr>
</tbody>
</table>
<p>The <strong>O(1)</strong> requirement is critical.</p>
<p>If operations become <strong>O(n)</strong>, the implementation is no longer optimal.</p>
<h2>Why Arrays Won’t Work</h2>
<p>A naive solution might store items in an array.</p>
<p>But arrays cause problems:</p>
<h3>Finding an item</h3>
<p>Requires scanning → <strong>O(n)</strong></p>
<h3>Moving an item</h3>
<p>Requires shifting elements → <strong>O(n)</strong></p>
<h3>Removing the least used item</h3>
<p>Also <strong>O(n)</strong></p>
<p>This violates the <strong>O(1)</strong> requirement.</p>
<p>So we need a better approach.</p>
<h2>The Key Insight: Combine Two Data Structures</h2>
<p>To achieve constant time operations, we combine:</p>
<h3>1. Hash Map (Map)</h3>
<p>Provides:</p>
<pre><code>key → node
</code></pre>
<p>Benefits:</p>
<ul>
<li>Instant lookup</li>
<li>O(1) access</li>
</ul>
<p>But a Map cannot track usage order.</p>
<h3>2. Doubly Linked List</h3>
<p>Maintains item order:</p>
<pre><code>Least used ←→ Most used
</code></pre>
<p>Benefits:</p>
<ul>
<li>Fast removal</li>
<li>Fast insertion</li>
<li>Fast reordering</li>
</ul>
<p>Operations on linked lists can be <strong>O(1)</strong> if you already have the node reference.</p>
<h2>Final Architecture</h2>
<p>We combine both structures:</p>
<pre><code>Map:
key → linked list node
Doubly Linked List:
[Least Recently Used ... Most Recently Used]
</code></pre>
<h3>Rules</h3>
<ul>
<li>Head = least recently used</li>
<li>Tail = most recently used</li>
</ul>
<h3>Step 1: Create a Linked List Node</h3>
<p>Each node stores:</p>
<ul>
<li>key</li>
<li>value</li>
<li>previous pointer</li>
<li>next pointer</li>
</ul>
<pre><code>class CacheNode {
constructor(key, value) {
this.key = key
this.value = value
this.prev = null
this.next = null
}
}
</code></pre>
<p>Important detail:</p>
<p>We store the key inside the node so we can remove it from the Map during eviction.</p>
<h3>Step 2: Implement a Doubly Linked List</h3>
<p>We’ll use sentinel (dummy) nodes for easier boundary handling.</p>
<pre><code>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
}
}
</code></pre>
<h3>Step 3: Build the LRU Cache</h3>
<p>Now we combine the Map and linked list.</p>
<pre><code>class LRUCache {
constructor(capacity) {
if (capacity <= 0) {
throw new Error("Capacity must be greater than zero")
}
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)
}
}
</code></pre>
<h3>Testing the Implementation</h3>
<p>Let's verify the behavior.</p>
<pre><code>function testLRUCache() {
const cache = new LRUCache(3)
cache.put("Algorithms", 1)
cache.put("Java", 2)
cache.put("Python", 3)
console.log(cache.get("Algorithms"))
// 1
cache.put("JavaScript", 4)
console.log(cache.get("Java"))
// -1 (evicted)
console.log(cache.get("JavaScript"))
// 4
cache.put("Python", 33)
console.log(cache.get("Python"))
// 33
}
testLRUCache()
</code></pre>
<p>If the output matches expectations, the cache works correctly.</p>
<h2>Common Mistakes Developers Make</h2>
<h3>1. Forgetting to store the key in nodes</h3>
<p>Without the key, eviction cannot remove the Map entry.</p>
<h3>2. Using a singly linked list</h3>
<p>Deleting nodes becomes <strong>O(n)</strong> because the previous node must be searched.</p>
<h3>3. Not using sentinel nodes</h3>
<p>This causes many edge cases when inserting or deleting.</p>
<h3>4. Forgetting to update the size counter</h3>
<p>This breaks capacity checks.</p>
<h3>5. Updating value without updating position</h3>
<p>Accessing an item sho…How to Build a Safe JSON Parser for Node.js APIshttps://jsdev.space/howto/safe-json-parser-nodejs/https://jsdev.space/howto/safe-json-parser-nodejs/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.Fri, 06 Mar 2026 00:00:00 GMT<p>Parsing JSON in Node.js looks trivial at first glance.</p>
<p>Most developers start with something like this:</p>
<pre><code>const data = JSON.parse(rawBody)
</code></pre>
<p>And for a while, that feels good enough.</p>
<p>Then real traffic arrives.</p>
<p>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 <code>JSON.parse()</code> call turns into a source of crashes, broken validation, or security issues.</p>
<p>That is why production APIs should not parse JSON blindly.</p>
<p>A safer approach is to build a JSON parser wrapper that handles the boring but critical work for you:</p>
<ul>
<li>reject oversized payloads</li>
<li>block dangerous keys</li>
<li>validate structure at runtime</li>
<li>return consistent errors</li>
<li>integrate cleanly with Express, Fastify, Hono, or custom Node.js servers</li>
</ul>
<p>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.</p>
<h2>Why a Wrapper Is Better Than Plain JSON.parse()</h2>
<p><code>JSON.parse()</code> is not bad. It is fast, built-in, and perfectly fine when the input is trusted.</p>
<p>The real problem is this:</p>
<blockquote>
<p>In APIs, input is often untrusted.</p>
</blockquote>
<p>That includes:</p>
<ul>
<li>request bodies</li>
<li>webhooks</li>
<li>uploaded JSON files</li>
<li>queue messages</li>
<li>Redis payloads</li>
<li>database JSON blobs</li>
<li>third-party callbacks</li>
</ul>
<p>When raw input comes from outside your system, parsing should do more than just convert a string into an object.</p>
<p>A proper parser layer should answer these questions:</p>
<ul>
<li>Is the payload too large?</li>
<li>Is the JSON valid?</li>
<li>Does it contain suspicious keys?</li>
<li>Does it match the shape the API expects?</li>
<li>Can the application return a clean error instead of crashing?</li>
</ul>
<p>That is exactly what a wrapper solves.</p>
<h2>What a Safe JSON Parser Should Do</h2>
<p>A strong implementation should provide five protections.</p>
<h3>1. Type checks</h3>
<p>Only strings or buffers should be accepted as raw JSON input.</p>
<h3>2. Payload size limits</h3>
<p>This helps reduce DoS risk and prevents huge request bodies from consuming memory.</p>
<h3>3. Dangerous key filtering</h3>
<p>Keys like <code>__proto__</code>, <code>constructor</code>, and <code>prototype</code> can become a problem in unsafe merge flows.</p>
<h3>4. Runtime validation</h3>
<p>Even valid JSON is not necessarily valid application data.</p>
<h3>5. Consistent error handling</h3>
<p>The API should return predictable, human-readable errors instead of generic crashes.</p>
<h2>The Core Design</h2>
<p>We will build the wrapper in layers.</p>
<p>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.</p>
<p>This approach keeps the code modular and easy to test.</p>
<h3>Step 1: Create Custom Error Types</h3>
<p>A parser wrapper becomes much easier to integrate when it throws structured errors instead of generic <code>Error</code> objects.</p>
<pre><code>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 = "JsonParseError"
this.statusCode = options?.statusCode ?? 400
this.code = options?.code ?? "INVALID_JSON"
this.details = options?.details
}
}
</code></pre>
<p>This gives the rest of the application useful metadata:</p>
<ul>
<li><code>statusCode</code> for HTTP responses</li>
<li><code>code</code> for machine-readable error handling</li>
<li><code>details</code> for debugging or validation reports</li>
</ul>
<h3>Step 2: Define Safe Defaults</h3>
<p>Now define the parser configuration.</p>
<pre><code>export type SafeJsonParserOptions = {
maxBytes?: number
forbiddenKeys?: string[]
}
const DEFAULT_FORBIDDEN_KEYS = ["__proto__", "constructor", "prototype"]
const DEFAULT_OPTIONS: Required<SafeJsonParserOptions> = {
maxBytes: 100 * 1024,
forbiddenKeys: DEFAULT_FORBIDDEN_KEYS,
}
</code></pre>
<p>These defaults are intentionally conservative.</p>
<p>A <code>100KB</code> default is reasonable for many APIs, but you can raise or lower it depending on your use case.</p>
<p>For example:</p>
<ul>
<li>small JSON commands: 10KB</li>
<li>standard API forms: 50KB to 200KB</li>
<li>large content payloads: 500KB to 1MB</li>
<li>file uploads: do not parse with raw <code>JSON.parse()</code> at all</li>
</ul>
<h3>Step 3: Normalize Raw Input</h3>
<p>In Node.js, raw request data may arrive as a string or a buffer.</p>
<p>A wrapper should handle both cleanly.</p>
<pre><code>function normalizeInput(raw: string | Buffer): string {
if (typeof raw === "string") {
return raw
}
if (Buffer.isBuffer(raw)) {
return raw.toString("utf8")
}
throw new JsonParseError("Unsupported input type", {
code: "INVALID_INPUT_TYPE",
})
}
</code></pre>
<p>This prevents accidental misuse and keeps the main parser logic simpler.</p>
<h3>Step 4: Enforce a Payload Size Limit</h3>
<p>Before calling <code>JSON.parse()</code>, check size first.</p>
<p>This is one of the easiest and most effective hardening steps.</p>
<pre><code>function assertPayloadSize(raw: string, maxBytes: number): void {
const byteLength = Buffer.byteLength(raw, "utf8")
if (byteLength > maxBytes) {
throw new JsonParseError(
`JSON payload exceeds the ${maxBytes} byte limit`,
{
statusCode: 413,
code: "PAYLOAD_TOO_LARGE",
details: { maxBytes, actualBytes: byteLength },
}
)
}
}
</code></pre>
<p>Why check bytes instead of <code>string.length</code>?</p>
<p>Because network payloads are measured in bytes, not characters. UTF-8 characters can take more than one byte, so <code>Buffer.byteLength()</code> is the safer choice.</p>
<h3>Step 5: Reject Dangerous Keys During Parsing</h3>
<p>The <code>reviver</code> argument of <code>JSON.parse()</code> lets you inspect every key-value pair during parsing.</p>
<p>That makes it a good place to block suspicious keys.</p>
<pre><code>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: "FORBIDDEN_JSON_KEY",
details: { key },
})
}
return value
}
}
</code></pre>
<p>This does not magically solve all object safety issues across your application, but it reduces risk significantly when dealing with untrusted payloads.</p>
<h3>Step 6: Build the Base Safe Parser</h3>
<p>Now combine the pieces into one reusable function.</p>
<pre><code>import { JsonParseError } from "./json-parse-error"
export type SafeJsonParserOptions = {
maxBytes?: number
forbiddenKeys?: string[]
}
const DEFAULT_FORBIDDEN_KEYS = ["__proto__", "constructor", "prototype"]
const DEFAULT_OPTIONS: Required<SafeJsonParserOptions> = {
maxBytes: 100 * 1024,
forbiddenKeys: DEFAULT_FORBIDDEN_KEYS,
}
function normalizeInput(raw: string | Buffer): string {
if (typeof raw === "string") {
return raw
}
if (Buffer.isBuffer(raw)) {
return raw.toString("utf8")
}
throw new JsonParseError("Unsupported input type", {
code: "INVALID_INPUT_TYPE",
})
}
function assertPayloadSize(raw: string, maxBytes: number): void {
const byteLength = Buffer.byteLength(raw, "utf8")
if (byteLength > maxBytes) {
throw new JsonParseError(
`JSON payload exceeds the ${maxBytes} byte limit`,
{
…Friday Links #35: Dev Tools, AI & JS Ecosystem Updateshttps://jsdev.space/friday/friday-35/https://jsdev.space/friday/friday-35/Discover the latest JavaScript tools, AI dev platforms, frameworks, and ecosystem updates in this week’s curated Friday Links roundup.Fri, 27 Feb 2026 00:00:00 GMT<p><img src="./images/friday-35.png" alt="Friday Links #35" /></p>
<p>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.</p>
<p>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.</p>
<p>Whether you'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.</p>
<h2>📜 Articles & Tutorials</h2>
<p><a href="https://nextjs.org/blog/agentic-future">Building Next.js for an agentic future</a></p>
<p><a href="https://adventures.nodeland.dev/archive/yes-learning-to-code-is-still-valuable">Yes, Learning to Code Is Still Valuable</a></p>
<p><a href="https://css-tricks.com/potentially-coming-to-a-browser-near-you/">Potentially Coming to a Browser :near() You </a></p>
<p><a href="https://frontendmasters.com/blog/death-to-scroll-fade/">Death to Scroll Fade!</a></p>
<p><a href="https://blog.logrocket.com/react-server-components-performance-mistakes/">6 React Server Component performance pitfalls in Next.js</a></p>
<p><a href="https://blog.hyperparam.app/hightable-scrolling-billions-of-rows/">Virtual Scrolling for Billions of Rows — Techniques from HighTable</a></p>
<p><a href="https://thecodebarbarian.com/getting-started-with-the-vercel-ai-sdk-in-nodejs.html">Getting Started with the Vercel AI SDK in Node.js</a></p>
<p><a href="https://stackinsight.dev/blog/loop-performance-empirical-study">Loop Performance Anti-Patterns: A 40-Repository Scan and Six-Module Benchmark Study</a></p>
<p><a href="https://aarontgrogg.github.io/NoLoJS/">Reduce the JS Workload with no- or lo-JS options</a></p>
<p><a href="https://stephaniewalter.design/blog/tips-on-how-to-pick-the-right-icons-for-your-website-with-icons8/">Tips on How to Pick the Right Icons for Your Website</a></p>
<p><a href="https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals">AGENTS.md outperforms skills in our agent evals</a></p>
<p><a href="https://una.im/border-shape">border-shape: the future of the non-rectangular web</a></p>
<p><a href="https://piccalil.li/blog/an-in-depth-guide-to-customising-lists-with-css/">An in-depth guide to customising lists with CSS</a></p>
<p><a href="https://css-tricks.com/loading-smarter-svg-vs-raster-loaders-in-modern-web-design/">Loading Smarter: SVG vs. Raster Loaders in Modern Web Design</a></p>
<h2>⚒️ Tools</h2>
<p><a href="https://www.react.doctor/">React Doctor</a> - 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.</p>
<p><a href="https://svar.dev/react/gantt/">SVAR React Gantt</a> - is a modern, high-performance Gantt chart component designed specifically for React applications.</p>
<p><a href="https://llm-timeline.com/">LLM Timeline</a></p>
<p><a href="https://oxc.rs/">The JavaScript Oxidation Compiler</a></p>
<p><a href="https://batiste.github.io/blop/example/">Blop</a> - 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.</p>
<p><a href="https://sciter.com/">Sciter</a> – Embeddable HTML/CSS/JavaScript Engine for modern UI development</p>
<p><a href="https://github.com/taskforcesh/bullmq">BullMQ</a> - Message Queue and Batch processing for NodeJS, Python, Elixir and PHP based on Redis</p>
<p><a href="https://github.com/tanstack/hotkeys">TanStack Hotkeys</a> - Type-Safe keyboard shortcuts library with awesome devtools</p>
<p><a href="https://github.com/Mina-Massoud/Mina-Rich-Editor">Mina Rich Editor</a> - 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.</p>
<p><img src="./images/Temporal-Playground.png" alt="Temporal Playground" /></p>
<p><a href="https://temporal-playground.vercel.app/">Temporal Playground</a> — 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.</p>
<p><a href="https://github.com/nicotsx/zerobyte">Zerobyte</a> - Powerful backup automation for your remote storage</p>
<h2>📚 Libs</h2>
<p><a href="https://github.com/LayoutitStudio/voxcss">voxcss</a> - A CSS voxel engine. A 3D grid for the DOM. Renders HTML cuboids by stacking grid layers and applying transforms.</p>
<p><a href="https://github.com/vercel-labs/portless">portless</a> - Replace port numbers with stable, named .localhost URLs. For humans and agents.</p>
<p><a href="https://github.com/tomkp/react-split-pane">react-split-pane</a> - React split-pane component</p>
<p><a href="https://github.com/patrickjuchli/basic-ftp">Basic FTP</a> - FTP client for Node.js, supports FTPS over TLS, passive mode over IPv6, async/await, and Typescript.</p>
<p><a href="https://github.com/sathvikc/lume-js">Lume.js</a> - Minimal reactive state management using only standard JavaScript and HTML. No custom syntax, no build step required, no framework lock-in.</p>
<p><a href="https://github.com/superlucky84/fp-pack">fp-pack</a> - A functional toolkit focused on type-safe pipelines, not FP dogma, for JavaScript and TypeScript.</p>
<p><a href="https://github.com/tambo-ai/tambo">tambo</a> - Generative UI SDK for React</p>
<h2>⌚ Releases</h2>
<p><a href="https://biomejs.dev/blog/biome-v2-4/">Biome v2.4—Embedded Snippets, HTML Accessibility, and Better Framework Support</a></p>
<p><a href="https://github.com/prisma/prisma/releases/tag/7.4.0">Prsma 7.4.0 Released</a></p>
<p><a href="https://phaser.io/news/2026/02/phaser-editor-v5-beta">Phaser Editor v5 Beta now available</a></p>
<p><a href="https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#502---022526">Emscripten 5.0.2</a> — 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.</p>
<p><a href="https://github.com/honojs/hono/releases/tag/v4.12.0">Hono 4.12</a> — 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.</p>
<p><a href="https://github.com/alfateam/orange-orm/releases/tag/v5.2.0">Orange ORM 5.2</a> — a powerful and modern ORM designed for efficient database interaction, offering type-safe queries, clean abstractions, and strong performance across modern JavaScript runtimes.</p>
<p><a href="https://eslint.org/blog/2026/02/eslint-v10.0.2-released/">ESLint 10.0.2 Released</a></p>
<h2>📺 Videos</h2>
<p><a href="https://www.youtube.com/watch?v=Ab01W6h4Giw">TanStack Router - How to Become a Routing God in React</a></p>
<p><a href="https://www.youtube.com/watch?v=0G_HKDrYpYc">Build Your Own Claude Code with Mastra Workspaces</a></p>
<p><a href="https://www.youtube.com/watch?v=uGsauG7Btlg">Build & Deploy AI Agent Workflow Builder using NextJs, Mongodb, React, Prisma, Upstash</a></p>
<p><a href="https://www.youtube.com/watch?v=O7DTIHISrJw">How One Engineer and AI Crashed IBM's Stock Price</a></p>
<p><a href="https://www.youtube.com/watch?v=ssYt09bCgUY">The wild rise of OpenClaw...</a></p>
<p><a href="https://www.youtube.com/watch?v=bzWI3Dil9Ig">My Multi-Agent Team with OpenClaw</a></p>
<p><a href="https://www.youtube.com/watch?v…SOLID Principles in React: SRP and OCPhttps://jsdev.space/react-solid-srp-ocp/https://jsdev.space/react-solid-srp-ocp/Learn how Single Responsibility and Open Closed principles apply to React components, hooks, and scalable frontend architecture.Thu, 26 Feb 2026 00:00:00 GMT<h2>SOLID Principles in React Architecture</h2>
<p>Software architecture almost never collapses instantly.</p>
<p>Most React applications begin in a perfectly reasonable state.</p>
<ul>
<li>A few components.</li>
<li>Some hooks.</li>
<li>Clean folders.</li>
<li>Readable logic.</li>
</ul>
<p>Everything feels simple.</p>
<p>Then reality arrives.</p>
<ul>
<li>New product requirements appear.</li>
<li>APIs evolve.</li>
<li>Design variants multiply.</li>
<li>State management spreads across the application.</li>
</ul>
<p>And suddenly something strange happens:</p>
<p>Changing one feature unexpectedly breaks three unrelated screens.</p>
<p>At this point developers usually blame React, state management, or framework decisions. But the real problem is almost always architectural.</p>
<p>The system stopped respecting <strong>responsibilities</strong>.</p>
<p>Read more: <a href="https://jsdev.space/solid-design-principles/">Understanding SOLID Principles</a></p>
<hr />
<h2>Why SOLID Still Matters in React</h2>
<p>SOLID principles were originally introduced for object-oriented programming by Robert C. Martin.</p>
<p>React is not object-oriented in the classical sense.</p>
<p>Yet modern React applications map surprisingly well to SOLID ideas:</p>
<table>
<thead>
<tr>
<th>OOP Concept</th>
<th>React Equivalent</th>
</tr>
</thead>
<tbody>
<tr>
<td>Class</td>
<td>Component</td>
</tr>
<tr>
<td>Method</td>
<td>Hook / handler</td>
</tr>
<tr>
<td>Dependency</td>
<td>Service / API</td>
</tr>
<tr>
<td>Composition</td>
<td>Component composition</td>
</tr>
<tr>
<td>Abstraction</td>
<td>Hook interface</td>
</tr>
</tbody>
</table>
<p>React didn’t remove architecture problems.</p>
<p>It simply changed where they appear.</p>
<p>Let’s explore the three SOLID principles that have the biggest real-world impact in React systems.</p>
<hr />
<h2>Single Responsibility Principle (SRP) in React</h2>
<p>Software architecture often sounds abstract until a project starts
growing. Messy components, duplicated logic, and unpredictable bugs
begin to appear.</p>
<p>The Single Responsibility Principle states:</p>
<blockquote>
<p>A module should have only one reason to change.</p>
</blockquote>
<p>In React, responsibilities usually split into:</p>
<ul>
<li>data logic</li>
<li>rendering</li>
<li>composition</li>
</ul>
<p>SRP <strong>does not mean</strong>:</p>
<ul>
<li>one function</li>
<li>small component</li>
<li>minimal lines of code</li>
</ul>
<p>Instead, it means:</p>
<p>A module should change for <strong>one category of reason</strong>.</p>
<h3>Bad Example</h3>
<pre><code>export function AccountProfile() {
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/profile")
.then(res => res.json())
.then(data => {
setProfile(data);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
return (
<section>
<h2>{profile.name}</h2>
<p>{profile.email}</p>
</section>
);
}
</code></pre>
<p>Component responsibilities:</p>
<ul>
<li>fetching data</li>
<li>managing state</li>
<li>rendering UI</li>
</ul>
<p>Multiple reasons to change.</p>
<h3>Correct SRP Architecture</h3>
<p>A scalable React architecture separates <strong>logic from presentation</strong>.</p>
<h3>Data Hook</h3>
<pre><code>export function useProfileData() {
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/profile")
.then(res => res.json())
.then(setProfile)
.finally(() => setLoading(false));
}, []);
return { profile, loading };
}
</code></pre>
<p>This hook answers one question:</p>
<p>How do we obtain data?</p>
<p>Nothing else.</p>
<h3>View Component</h3>
<pre><code>export function ProfileView({ profile, loading }) {
if (loading) return <p>Loading...</p>;
return (
<section>
<h2>{profile.name}</h2>
<p>{profile.email}</p>
</section>
);
}
</code></pre>
<p>Pure UI.</p>
<p>No knowledge about APIs or storage.</p>
<h3>Container</h3>
<pre><code>export function ProfileContainer() {
const state = useProfileData();
return <ProfileView {...state} />;
}
</code></pre>
<p>Now each part has one responsibility.</p>
<p>Changing backend logic never affects rendering.</p>
<hr />
<h2>Real SRP Benefit</h2>
<p>SRP enables:</p>
<ul>
<li>safer refactoring</li>
<li>reusable UI components</li>
<li>independent testing</li>
<li>predictable scaling</li>
</ul>
<p>Large React applications survive long-term mainly because of this separation.</p>
<h2>Open Closed Principle (OCP) in React</h2>
<p>The Open Closed Principle states:</p>
<blockquote>
<p>Software entities should be open for extension but closed for
modification.</p>
</blockquote>
<p>You should add new behavior without rewriting existing components.</p>
<h3>Typical Violation</h3>
<pre><code>export function ActionButton({ type, onClick }) {
if (type === "primary") {
return <button className="primary" onClick={onClick}>Primary</button>;
}
if (type === "danger") {
return <button className="danger" onClick={onClick}>Danger</button>;
}
if (type === "success") {
return <button className="success" onClick={onClick}>Success</button>;
}
return null;
}
</code></pre>
<p>Every new variation requires modifying the component.</p>
<p>Over time this becomes fragile and error-prone.</p>
<h3>OCP-Friendly Approach</h3>
<pre><code>const buttonVariants = {
primary: "primary",
danger: "danger",
success: "success",
};
export function ActionButton({ variant, onClick, children }) {
return (
<button
className={buttonVariants[variant]}
onClick={onClick}
>
{children}
</button>
);
}
</code></pre>
<p>Adding new behavior:</p>
<pre><code>buttonVariants.warning = "warning";
</code></pre>
<p>No component modification required.</p>
<h3>Composition-Based Extension</h3>
<p>Modern React strongly favors composition.</p>
<pre><code>export function Button({ className, ...props }) {
return <button className={className} {...props} />;
}
export function DangerButton(props) {
return <Button className="danger" {...props} />;
}
export function PrimaryButton(props) {
return <Button className="primary" {...props} />;
}
</code></pre>
<p>The base abstraction remains stable while functionality grows externally.</p>
<p>This is OCP applied naturally.</p>
<hr />
<h2>Dependency Inversion Principle (DIP)</h2>
<p>This principle separates scalable architecture from tightly coupled applications.</p>
<p>Definition:</p>
<blockquote>
<p>High-level modules should not depend on low-level modules.</p>
</blockquote>
<p>Translated to React:</p>
<p>UI should not depend directly on infrastructure.</p>
<h2>Hidden Dependency Problem</h2>
<pre><code>export function OrdersPage() {
const [orders, setOrders] = useState([]);
useEffect(() => {
fetch("/api/orders")
.then(r => r.json())
.then(setOrders);
}, []);
}
</code></pre>
<p>This component depends directly on:</p>
<ul>
<li>transport layer</li>
<li>backend structure</li>
<li>API implementation</li>
</ul>
<p>Backend change → UI rewrite.</p>
<h2>Introducing Abstraction</h2>
<h3>Provider Interface</h3>
<pre><code>export interface OrdersProvider {
getOrders(): Promise<any[]>;
}
</code></pre>
<h3>Concrete Implementation</h3>
<pre><code>export class ApiOrdersProvider implements OrdersProvider {
async getOrders() {
const res = await fetch("/api/orders");
return res.json();
}
}
</code></pre>
<h3>Hook Depends on Abstraction</h3>
<pre><code>export function useOrders(provider: OrdersProvider) {
const [orders, setOrders] = useState([]);
useEffect(() => {
provider.getOrders().then(setOrders);
}, [provider]);
return orders;
}
</code></pr…SQL Crash Course: JOINs, CTEs, and Window Functionshttps://jsdev.space/sql-complete-guide/https://jsdev.space/sql-complete-guide/A complete modern SQL guide covering SELECT, filtering, sorting, aggregates, JOINs, DML/DDL, CTEs, execution order, indexes, and window functions.Mon, 23 Feb 2026 00:00:00 GMT<p>SQL is one of the very few technologies in software engineering that
does not fade with trends.</p>
<p>Frameworks change every few years. Backend stacks rotate. Frontend
ecosystems reinvent themselves. But SQL stays.</p>
<p>That is not nostalgia. That is infrastructure.</p>
<p>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.</p>
<p>Understanding SQL today is not optional for serious developers.</p>
<p>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.</p>
<p>This article walks through SQL step by step --- from simple SELECT
queries to advanced window functions --- using practical examples and a
PostgreSQL‑friendly syntax.</p>
<hr />
<h2>1. Database Anatomy: What We Actually Work With</h2>
<p>Before writing queries, it helps to understand what the database really is.</p>
<p>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.</p>
<p>Each table contains:</p>
<ul>
<li>Columns (structure)</li>
<li>Rows (actual data)</li>
</ul>
<p>The power comes from constraints and relationships.</p>
<p>Unlike spreadsheets, relational databases enforce:</p>
<p>. Strong typing
. Explicit relationships</p>
<h3>Primary Keys and Foreign Keys</h3>
<p>Primary Key (PK) --- unique row identifier.</p>
<p>Foreign Key (FK) --- reference to another table's primary key.</p>
<p>Example:</p>
<ul>
<li><code>users(id, name)</code></li>
<li><code>orders(id, user_id, total)</code></li>
</ul>
<p><code>orders.user_id</code> references <code>users.id</code>.</p>
<p>This guarantees consistency: the database will reject an order for a
non‑existent user.</p>
<h3>Common Data Types</h3>
<p>Most projects rely on a core set:</p>
<ul>
<li>INT / BIGINT --- numeric identifiers and counters</li>
<li>TEXT / VARCHAR --- strings</li>
<li>TIMESTAMPTZ --- date-time stored in UTC</li>
<li>JSONB (PostgreSQL) --- flexible semi‑structured data</li>
</ul>
<p>Always store timestamps in UTC. Convert on the client side.</p>
<hr />
<h2>2. Reading Data: SELECT</h2>
<p>The most common SQL operation is reading.</p>
<p>Basic query:</p>
<pre><code>SELECT name, price
FROM products;
</code></pre>
<p>Avoid:</p>
<pre><code>SELECT *
FROM products;
</code></pre>
<p>Why? Because production systems scale. Fetching unnecessary columns wastes bandwidth, memory, and sometimes performance advantages from indexes.</p>
<h3>Aliases</h3>
<pre><code>SELECT
name AS product_name,
price AS product_price
FROM products;
</code></pre>
<p>This is especially helpful in complex JOIN queries.</p>
<h3>DISTINCT</h3>
<p>Use DISTINCT when you care about unique values, not raw rows.</p>
<pre><code>SELECT DISTINCT category
FROM products;
</code></pre>
<h3>Pagination</h3>
<pre><code>SELECT id, name
FROM products
ORDER BY id
LIMIT 10 OFFSET 20;
</code></pre>
<p>Large OFFSET values are inefficient. Prefer keyset pagination in
high‑load systems:</p>
<pre><code>SELECT id, name
FROM products
WHERE id > 1000
ORDER BY id
LIMIT 10;
</code></pre>
<p>OFFSET works for small pages. For large datasets, keyset pagination is more efficient.</p>
<hr />
<h2>3. Filtering and Sorting</h2>
<p>Filtering uses WHERE.</p>
<p>Filtering narrows data down to what actually matters.</p>
<p>Instead of loading everything and filtering in application code, push logic into the database — it is optimized for this.</p>
<pre><code>SELECT name, price
FROM products
WHERE category = 'phones'
AND price > 50000;
</code></pre>
<h3>IN</h3>
<p>Cleaner than multiple OR statements.</p>
<pre><code>WHERE category IN ('phones', 'laptops')
</code></pre>
<h3>BETWEEN</h3>
<p>Readable range filter.</p>
<pre><code>WHERE price BETWEEN 30000 AND 60000
</code></pre>
<h3>LIKE / ILIKE</h3>
<p>Pattern matching for text.</p>
<pre><code>WHERE name LIKE 'iPhone%'
</code></pre>
<p>Postgres case-insensitive:</p>
<pre><code>WHERE name ILIKE 'iphone%'
</code></pre>
<h3>NULL Handling</h3>
<p>NULL means “unknown”, not empty.</p>
<p>Incorrect:</p>
<pre><code>WHERE description = NULL
</code></pre>
<p>Correct:</p>
<pre><code>WHERE description IS NULL
</code></pre>
<h3>ORDER BY</h3>
<p>Sorting is expensive on large datasets — especially without indexes.</p>
<pre><code>SELECT name, price
FROM products
ORDER BY price DESC, name ASC;
</code></pre>
<p>Sorting large datasets without indexes is expensive.</p>
<hr />
<h2>4. Aggregation and GROUP BY</h2>
<p>Aggregation turns raw data into insight.</p>
<p>Instead of looking at thousands of rows, we compute metrics.</p>
<p><img src="./images/aggregation-css.png" alt="Aggregation and GROUP BY" /></p>
<p>Aggregate functions:</p>
<ul>
<li>COUNT</li>
<li>SUM</li>
<li>AVG</li>
<li>MIN</li>
<li>MAX</li>
</ul>
<p>Example:</p>
<pre><code>SELECT
MAX(price) AS max_price,
AVG(price) AS avg_price,
COUNT(*) AS total_products
FROM products;
</code></pre>
<h3>GROUP BY</h3>
<p>Grouping splits rows into logical buckets before aggregation.</p>
<pre><code>SELECT
category,
COUNT(*) AS product_count,
AVG(price) AS avg_price
FROM products
GROUP BY category;
</code></pre>
<p>Rule:</p>
<p>Every non-aggregated column in SELECT must appear in GROUP BY.</p>
<h3>HAVING</h3>
<p>HAVING filters groups after aggregation — unlike WHERE.</p>
<p>Incorrect:</p>
<pre><code>SELECT category, COUNT(*)
FROM products
WHERE COUNT(*) > 10
GROUP BY category;
</code></pre>
<p>Correct:</p>
<pre><code>SELECT category, COUNT(*)
FROM products
GROUP BY category
HAVING COUNT(*) > 10;
</code></pre>
<hr />
<h2>5. JOINs</h2>
<p>Real SQL begins with JOINs.</p>
<p><img src="./images/sql-joins.png" alt="JOINs in SQL" /></p>
<p>JOINs connect tables. This is where relational databases shine.</p>
<p>Instead of duplicating data, we combine normalized structures dynamically.</p>
<h3>INNER JOIN</h3>
<pre><code>SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id;
</code></pre>
<p>Only matching rows are returned.</p>
<h3>LEFT JOIN</h3>
<pre><code>SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
</code></pre>
<p>All users appear. Missing matches become NULL.</p>
<p>Find users without orders:</p>
<pre><code>SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
</code></pre>
<h3>SELF JOIN</h3>
<p>Used when a table references itself (hierarchies, managers, categories).</p>
<pre><code>SELECT e.name AS employee,
m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
</code></pre>
<hr />
<h2>6. Data Modification (DML)</h2>
<p>These queries change actual data.</p>
<p>Use carefully.</p>
<p><img src="./images/data-modification.png" alt="Data Modification" /></p>
<h3>INSERT</h3>
<pre><code>INSERT INTO users (name, email)
VALUES ('Alex', '[email protected]');
</code></pre>
<p>Always list columns.</p>
<h3>UPDATE</h3>
<pre><code>UPDATE products
SET price = 65000
WHERE id = 42;
</code></pre>
<p>Never run UPDATE without WHERE unless intentional.</p>
<h3>DELETE</h3>
<pre><code>DELETE FROM users
WHERE last_login < '2020-01-01';
</code></pre>
<h3>TRUNCATE</h3>
<pre><code>TRUNCATE TABLE logs;
</code></pre>
<p>Instantly removes all rows.</p>
<hr />
<h2>7. Schema Changes (DDL)</h2>
<p>DDL modifies structure, not data.</p>
<p>Think of it as plumbing — not water.</p>
<h3>CREATE TABLE</h3>
<p>Defines structure and constraints.</p>
<pre><code>CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
total NUMERIC(10,2) DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
</code></pre>
<h3>ALTER TABLE</h3>
<p>Adds or modifies columns safely.</p>
<pre><code>ALTER TABLE orders
ADD COLUMN promo_code TEXT;
</code></pre>
<h3>DROP TABLE</h3>
<pre><code>DROP TABLE orders;
</code></pre>
<…Polymorphic Decorators in React: HOCs on Steroidshttps://jsdev.space/polymorphic-hocs-ts/https://jsdev.space/polymorphic-hocs-ts/A deep dive into type-safe polymorphic decorators (HOCs) in React with clean logic composition, reusable routing, and advanced TypeScript patterns.Sun, 22 Feb 2026 00:00:00 GMT<p>Modern React applications rarely fail because of rendering complexity.
They fail because logic spreads across components in unpredictable ways.</p>
<p>Analytics. Routing. Permissions. Feature flags. Loading states. A/B
testing. Logging.</p>
<p>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.</p>
<p>This article explores a production-grade architectural approach:</p>
<p><strong>Polymorphic decorators implemented as strongly typed Higher-Order
Components (HOCs).</strong></p>
<p>The objectives:</p>
<ul>
<li>Separate logic from rendering</li>
<li>Preserve strict TypeScript safety</li>
<li>Enable reusable composition</li>
<li>Avoid JSX nesting hell</li>
<li>Scale across large React applications</li>
</ul>
<p>This is not theory. Everything Here is practical and
production-oriented.</p>
<hr />
<h2>The Core Problem: JSX Nesting Explosion</h2>
<p>Imagine a simple requirement.</p>
<p>A button must:</p>
<p>. Generate an href from <code>/product/:id</code>
. Inject route parameters
. Send analytics on click
. Stay polymorphic
. Remain fully type-safe</p>
<p>The typical JSX composition quickly becomes unreadable:</p>
<pre><code><TrackClick
as={(props) => (
<ResolveRoute
as={PrimaryButton}
template="/product/:id"
params={{ id: "42" }}
{...props}
/>
)}
elementKey="hero-btn"
eventLabel="hero_click"
/>
</code></pre>
<p>Or using asChild-style composition:</p>
<pre><code><ResolveRoute template="/product/:id" params={{ id: "42" }} asChild>
<TrackClick elementKey="hero-btn" eventLabel="hero_click" asChild>
<PrimaryButton variant="solid">
Buy Now
</PrimaryButton>
</TrackClick>
</ResolveRoute>
</code></pre>
<p>Even though it works, readability suffers.</p>
<p>Now compare with:</p>
<pre><code>const SmartButton = withRouteResolver(
withAnalyticsTracking(PrimaryButton)
)
<SmartButton
template="/product/:id"
params={{ id: "42" }}
elementKey="hero-btn"
eventLabel="hero_click"
variant="solid"
/>
</code></pre>
<p>Flat. Predictable. Maintainable.</p>
<hr />
<h2>Turning Polymorphic Components into Decorators</h2>
<p>Classic polymorphic component:</p>
<pre><code>import {
ElementType,
createElement,
MouseEventHandler,
} from "react"
type MergeProps<Base, Extra> =
Omit<Base, keyof Extra> & Extra
type PropsOf<T extends ElementType> =
React.ComponentPropsWithoutRef<T>
function WithSideEffect<
TComponent extends ElementType<{ onClick?: MouseEventHandler }>
>({
as,
onClick,
sideEffect,
...rest
}: MergeProps<
PropsOf<TComponent>,
{
as: TComponent
sideEffect?: MouseEventHandler
onClick?: MouseEventHandler
}
>) {
const handleClick: MouseEventHandler = (event) => {
onClick?.(event)
sideEffect?.(event)
}
return createElement(as, {
...rest,
onClick: handleClick,
})
}
</code></pre>
<p>The transformation is simple:</p>
<p>Remove <code>as</code> from props and accept the component as a function parameter.</p>
<p>That converts polymorphic behavior into a reusable decorator.</p>
<hr />
<h2>Analytics Decorator (Production-Ready)</h2>
<pre><code>import { ComponentType, MouseEventHandler } from "react"
type AnalyticsProps = {
elementKey: string
eventLabel?: string
}
export function withAnalyticsTracking<
TBase extends {
onClick?: MouseEventHandler
label?: string
}
>(BaseComponent: ComponentType<TBase>) {
return function AnalyticsWrapped(
props: TBase & AnalyticsProps
) {
const {
elementKey,
eventLabel,
onClick,
...rest
} = props
const handleClick: MouseEventHandler = (event) => {
console.log("Tracking event:", {
elementKey,
label: eventLabel ?? props.label ?? elementKey,
})
onClick?.(event)
}
return (
<BaseComponent
{...(rest as TBase)}
onClick={handleClick}
/>
)
}
}
</code></pre>
<p>This decorator:</p>
<ul>
<li>Preserves original props</li>
<li>Injects analytics</li>
<li>Keeps type inference intact</li>
<li>Works with any compatible component</li>
</ul>
<hr />
<h2>Route Resolver Decorator</h2>
<p>Centralized routing logic prevents chaos.</p>
<pre><code>type RouteProps<T extends string> = {
template: T
params: Record<string, string>
}
export function withRouteResolver<
TBase extends { href?: string }
>(BaseComponent: ComponentType<TBase>) {
return function RouteWrapped<
TTemplate extends string
>(props: TBase & RouteProps<TTemplate>) {
const { template, params, ...rest } = props
const resolvedHref = Object.entries(params).reduce(
(url, [key, value]) =>
url.replace(`:${key}`, value),
template
)
return (
<BaseComponent
{...(rest as TBase)}
href={resolvedHref}
/>
)
}
}
</code></pre>
<p>This enables:</p>
<ul>
<li>Template validation</li>
<li>Central formatting</li>
<li>Localization integration</li>
<li>Analytics chaining</li>
<li>Strict parameter control</li>
</ul>
<hr />
<h2>Composing Decorators Safely</h2>
<pre><code>const EnhancedButton = withRouteResolver(
withAnalyticsTracking(PrimaryButton)
)
</code></pre>
<p>Composition remains predictable because:</p>
<ul>
<li>Each decorator isolates logic</li>
<li>Rendering remains untouched</li>
<li>TypeScript enforces compatibility</li>
</ul>
<hr />
<h2>Building a Decoratable Card System</h2>
<p>Base card:</p>
<pre><code>type CardBaseProps = {
className?: string
style?: React.CSSProperties
children?: React.ReactNode
}
export function CardBase({
className,
style,
children,
}: CardBaseProps) {
return (
<div
className={`rounded-lg p-4 shadow ${className ?? ""}`}
style={style}
>
{children}
</div>
)
}
</code></pre>
<p>Loading decorator:</p>
<pre><code>export function withLoadingState<
T extends { children?: React.ReactNode }
>(BaseComponent: ComponentType<T>) {
return function LoadingWrapped(
props: T & { isLoading?: boolean }
) {
const { isLoading, children, ...rest } = props
return (
<BaseComponent {...(rest as T)}>
{isLoading ? <div>Loading...</div> : children}
</BaseComponent>
)
}
}
</code></pre>
<p>Compose utility:</p>
<pre><code>function compose(
...decorators: Array<
(component: ComponentType<any>) => ComponentType<any>
>
) {
return (base: ComponentType<any>) =>
decorators.reduceRight(
(acc, decorator) => decorator(acc),
base
)
}
</code></pre>
<p>Final card:</p>
<pre><code>export const Card = compose(
withLoadingState
)(CardBase)
</code></pre>
<hr />
<h2>TypeScript Limitation: Generic Collisions</h2>
<p>Decorators work perfectly --- until multiple introduce computed
generics.</p>
<p>Example:</p>
<pre><code>return function WithRoute<TTemplate extends string>(
props: ExtractProps<TBase> & LinkProps<TTemplate>
)
</code></pre>
<p>Stacking several generic-heavy decorators may:</p>
<ul>
<li>Collapse inference</li>
<li>Override template literal types</li>
<li>Reduce safety guarantees</li>
</ul>
<p>Practical rule:</p>
<p>Use only one computed-generic decorator per chain.</p>
<hr />
<h2>Where This Pattern Excels</h2>
<p>Ideal for:</p>
<ul>
<li>Analytics</li>
<li>Logging</li>
<li>Permission layers</li>
<li>Feature flags</li>
<li>Centralized routing</li>
<li>Business rule injection</li>
<li>UI library extensions</li>
</ul>
<p>Less ideal for:</p>
<ul>
<li>Highly dynamic render patterns (FACC is stronger there)</li>
</ul>
<hr />
<h2>Architectural Impact</h2>
<p>In large React + TypeScript applications:</p>
<ul>
<li>Components are not always under your cont…Nexus State: A Modern Atomic State Manager for JavaScripthttps://jsdev.space/nexus-state-manager/https://jsdev.space/nexus-state-manager/Deep technical overview of Nexus State, a minimal atomic state manager with async atoms, middleware, persistence, and built-in DevTools.Fri, 20 Feb 2026 00:00:00 GMT<p>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.</p>
<p>Nexus State introduces an atomic model built around simplicity while still offering powerful capabilities such as:</p>
<ul>
<li>Built-in DevTools</li>
<li>Time Travel debugging</li>
<li>Async state primitives</li>
<li>Persistence plugins</li>
<li>Middleware system</li>
<li>Multi-framework support</li>
</ul>
<p>This article explores its architecture, core patterns, and practical usage.</p>
<hr />
<h2>Core Concept: Atoms</h2>
<p>Atoms are minimal, isolated pieces of state.</p>
<pre><code>import { atom, createStore } from '@nexus-state/core'
const sessionCounter = atom(0, 'sessionCounter')
const multipliedValue = atom((read) => read(sessionCounter) * 5, 'multipliedValue')
const mainStore = createStore()
mainStore.set(sessionCounter, 4)
console.log(mainStore.get(multipliedValue)) // 20
</code></pre>
<p>Derived atoms recompute automatically when dependencies change.</p>
<hr />
<h2>Store & Subscriptions</h2>
<pre><code>const unsubscribeHandler = mainStore.subscribe(sessionCounter, (value) => {
console.log('Updated:', value)
})
mainStore.set(sessionCounter, 12)
unsubscribeHandler()
</code></pre>
<p>The store handles dependency tracking and batched updates.</p>
<hr />
<h2>React Example</h2>
<pre><code>import { useAtom } from '@nexus-state/react'
const progressAtom = atom(0, 'progress')
const percentAtom = atom((read) => read(progressAtom) * 10, 'percent')
export function ProgressPanel() {
const [progress, updateProgress] = useAtom(progressAtom)
const [percent] = useAtom(percentAtom)
return (
<div>
<h2>Progress: {progress}</h2>
<p>Percentage: {percent}%</p>
<button onClick={() => updateProgress(prev => prev + 1)}>
Increase
</button>
</div>
)
}
</code></pre>
<p>Only components depending on changed atoms re-render.</p>
<hr />
<h2>Async Atoms</h2>
<pre><code>import { asyncAtom } from '@nexus-state/async'
const [accountAtom, fetchAccount] = asyncAtom({
fetchFn: async () => {
const response = await fetch('/api/account')
return response.json()
}
})
</code></pre>
<p>Async atoms encapsulate loading, error, and resolved states.</p>
<hr />
<h2>Persistence Example</h2>
<pre><code>import { persist, localStorageStorage } from '@nexus-state/persist'
persist(progressAtom, {
key: 'app_progress',
storage: localStorageStorage
})(mainStore)
</code></pre>
<p>State is automatically restored on reload.</p>
<hr />
<h2>Middleware Example</h2>
<pre><code>import { middleware } from '@nexus-state/middleware'
const auditMiddleware = middleware(progressAtom, {
beforeSet: (_, next) => {
console.log('Before:', next)
return next
},
afterSet: (_, next) => {
console.log('After:', next)
}
})
</code></pre>
<hr />
<h2>Performance Characteristics</h2>
<p>Nexus State optimizes:</p>
<ul>
<li>Selective component updates</li>
<li>Batched state mutations</li>
<li>Lazy evaluation of derived atoms</li>
<li>Zero DevTools overhead in production builds</li>
</ul>
<hr />
<h2>Comparison Snapshot</h2>
<p>Compared to Redux:</p>
<ul>
<li>Less boilerplate</li>
<li>No action types</li>
<li>Native async support</li>
<li>Smaller bundle</li>
</ul>
<p>Compared to Zustand:</p>
<ul>
<li>Built-in Time Travel</li>
<li>Multi-framework support</li>
</ul>
<p>Compared to MobX:</p>
<ul>
<li>Explicit dependency graph</li>
<li>Smaller runtime size</li>
</ul>
<p>Compared to Jotai:</p>
<ul>
<li>Store instance isolation</li>
<li>Built-in persistence</li>
</ul>
<hr />
<h2>When Nexus State Makes Sense</h2>
<p>Use it when:</p>
<ul>
<li>You want atomic isolation</li>
<li>You need computed state out of the box</li>
<li>Debugging tools matter</li>
<li>You work across multiple frameworks</li>
<li>You build micro-frontends</li>
</ul>
<hr />
<h2>Resources</h2>
<p>Documentation: <a href="https://nexus-state.website.yandexcloud.net">nexus-state.website.yandexcloud.net</a></p>
<p>Source Code: <a href="https://sourcecraft.dev/astashkin-a/nexus-state">sourcecraft.dev/astashkin-a/nexus-state</a></p>
<p>Demo Applications: <a href="https://codesandbox.io/p/sandbox/nryqsj">codesandbox.io/p/sandbox/nryqsj</a></p>
<h2>Related articles</h2>
<ul>
<li><a href="/algorithm-complexity-big-o/">Understanding Big O Notation with JavaScript</a></li>
<li><a href="/animejs-animation-guide/">Mastering Web Animations with Anime.js</a></li>
<li><a href="/await-fetch-slow/">Optimizing await fetch(): Why It Slows Down and How to Speed It Up</a></li>
</ul>
How to Fix Circular Imports in a React/TypeScript Applicationhttps://jsdev.space/howto/circular-imports-ts/https://jsdev.space/howto/circular-imports-ts/Learn how to detect, understand, and systematically fix circular imports in a React + TypeScript project. Practical examples, module structure diagrams...Fri, 20 Feb 2026 00:00:00 GMT<p>Circular (recursive) imports are one of those issues that look small at first — until they start breaking runtime behavior, tests, hot reload, or production builds.</p>
<p>They rarely appear as a single obvious mistake. More often, they are a symptom of deeper architectural problems: unclear module boundaries, excessive barrel files, and uncontrolled cross-feature dependencies.</p>
<p>In this guide, we’ll walk through a practical approach to:</p>
<ul>
<li>Detect circular imports</li>
<li>Understand why they happen</li>
<li>Refactor the structure safely</li>
<li>Prevent them from coming back</li>
</ul>
<hr />
<h2>A Real Module Structure Example</h2>
<pre><code>modules/
client/
index.ts # Public API of the module
redux/
actions.ts
reducer.ts
types.ts
index.ts
ui/
ClientComponent.tsx
service.ts
types.ts
</code></pre>
<p>This is a common structure:</p>
<ul>
<li><code>redux/</code> contains state logic</li>
<li><code>ui/</code> contains components</li>
<li><code>service.ts</code> contains business logic</li>
<li><code>index.ts</code> exposes the public API</li>
</ul>
<p>Nothing looks wrong at first glance. But this structure can easily create circular dependencies if not handled carefully.</p>
<hr />
<h2>Barrel Files: The Hidden Source of Cycles</h2>
<p>Barrel files (<code>index.ts</code>) are useful — but dangerous when overused.</p>
<h3>The Common Mistake</h3>
<pre><code>modules/index.ts
export * from "./client";
export * from "./order";
</code></pre>
<p>This expands your dependency graph and increases the risk of circular imports.</p>
<p>Instead of importing specific modules, developers start importing from <code>@modules</code>, which forces evaluation of everything.</p>
<hr />
<h2>Correct vs Incorrect Imports</h2>
<h3>Correct</h3>
<pre><code>import { clientActions } from "@modules/client";
</code></pre>
<h3>Incorrect</h3>
<pre><code>import { clientActions } from "@modules";
</code></pre>
<p>The second option hides the dependency path and makes cycles easier to create accidentally.</p>
<hr />
<h2>Internal Module Imports: Always Use Relative Paths</h2>
<h3>Incorrect</h3>
<pre><code>import { clientActions } from ".";
</code></pre>
<h3>Correct</h3>
<pre><code>import { clientActions } from "./redux/actions";
import { clientTypes } from "./types";
</code></pre>
<p>Always use direct relative imports inside the same module.</p>
<hr />
<h2>High Cohesion: Export Only What Is Public</h2>
<h3>Incorrect</h3>
<pre><code>export { clientActions, clientReducer } from "./redux";
export { ClientComponent } from "./ui";
export { clientTypes } from "./types";
</code></pre>
<p>This exposes internal implementation details.</p>
<h3>Correct</h3>
<pre><code>export { ClientService } from "./service";
export type { Client } from "./types";
</code></pre>
<p>Only export what external modules truly need.</p>
<hr />
<h2>Understanding Dependency Graphs</h2>
<pre><code>utils/ ← leaf node
├─ index.ts
├─ format.ts
└─ validate.ts
modules/
├─ client/ ← depends on utils
│ └─ index.ts
└─ order/ ← depends on client and utils
└─ index.ts
</code></pre>
<p>Safe dependency direction:</p>
<pre><code>utils → client → order
</code></pre>
<p>No back-references. No cycles.</p>
<hr />
<h2>Example of a Circular Dependency</h2>
<h3>Before</h3>
<pre><code>client/service.ts
import { orderService } from "@modules/order";
order/service.ts
import { clientService } from "@modules/client";
</code></pre>
<p>Dependency graph:</p>
<pre><code>client → order → client
</code></pre>
<hr />
<h2>How to Fix It</h2>
<h3>Option 1: Extract Shared Logic</h3>
<pre><code>@modules/shared/types.ts
export type { Client, Order };
</code></pre>
<p>Now both modules depend on shared:</p>
<pre><code>client → shared
order → shared
</code></pre>
<hr />
<h3>Option 2: Split Large Modules</h3>
<p>Break a feature into:</p>
<ul>
<li>client-core (business logic)</li>
<li>client-ui (UI components)</li>
</ul>
<p>Then:</p>
<pre><code>import { clientCoreService } from "@modules/client-core";
</code></pre>
<p>Smaller modules reduce coupling.</p>
<hr />
<h2>Detecting Circular Imports</h2>
<p>Recommended tools:</p>
<ul>
<li>madge</li>
<li>dependency-cruiser</li>
<li>eslint-plugin-import</li>
</ul>
<hr />
<h2>Final Thoughts</h2>
<p>Circular imports are not just annoying errors — they are architectural feedback.</p>
<p>A clean dependency graph:</p>
<ul>
<li>Improves maintainability</li>
<li>Makes testing easier</li>
<li>Reduces unpredictable runtime behavior</li>
<li>Keeps teams aligned</li>
</ul>
<p>The real goal isn’t just “no circular imports”.</p>
<p>It’s a clean, predictable module architecture.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/env-ts-zod/">Validate ENV Variables in TypeScript with Zod</a></li>
<li><a href="/howto/flatmap-doesnt-exist/">Resolving Missing flat, flatMap, and Flatten on any[] in TypeScript</a></li>
<li><a href="/howto/global-vars-ts/">Declaring and Managing Global Variables in TypeScript</a></li>
</ul>
Friday Links #34: Modern JavaScript Picks & Highlightshttps://jsdev.space/friday/friday-34/https://jsdev.space/friday/friday-34/A curated roundup of modern JavaScript tools, libraries, releases, and dev resources worth exploring this week — practical picks and notable updates.Fri, 13 Feb 2026 00:00:00 GMT<p><img src="./images/friday-34.png" alt="Friday Links #34" /></p>
<p>Friday Links #33 brings together a fresh set of modern JavaScript highlights — from new tools and framework updates to useful libraries and developer resources. This edition focuses on practical discoveries you can try immediately, plus a few notable releases that may shape upcoming workflows. If you like staying current without scrolling through endless feeds, this digest is for you.</p>
<h2>Pinterest processes more searches than ChatGPT</h2>
<p><img src="./images/pinterest.png" alt="Pinterest" /></p>
<p>According to Pinterest CEO Bill Ready, who made the comparison while discussing the company’s latest quarterly results. He positioned Pinterest as a major standalone search and discovery entry point, especially for commercial intent.</p>
<p>According to third-party estimates, ChatGPT handles around 75B searches per month, while Pinterest sees roughly 80B, generating about 1.7B monthly clicks. Ready noted that more than half of Pinterest searches are commercial in nature, versus roughly 2% for ChatGPT (by his estimate).</p>
<p>The quarter itself came in slightly below expectations:
— revenue: $1.32B vs $1.33B expected
— EPS: $0.67 vs $0.69 expected
— Q1 2026 outlook: $951–971M vs $980M expected</p>
<p>Pinterest attributed the softness to reduced advertiser budgets (especially in Europe) and new tariffs affecting home and furniture categories. Despite this, user growth beat forecasts, reaching 619M monthly active users (+12% YoY). Shares dropped about 20% in after-hours trading.</p>
<p>The company says it’s doubling down on visual search, recommendations, personalization, and tighter e-commerce integrations (including Amazon partnerships) to capture buying intent earlier in the discovery journey.</p>
<h2>📜 Articles & Tutorials</h2>
<p><a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-deep-think/">Gemini 3 Deep Think: Advancing science, research and engineering</a></p>
<p><a href="https://forwardemail.net/en/blog/docs/email-protocols-rfc-compliance-imap-smtp-pop3-comparison">Email RFC Protocol Support - Complete Standards & Specifications Guide</a></p>
<p><a href="https://blog.logrocket.com/css-in-2026">CSS in 2026: The new features reshaping frontend development</a></p>
<p><a href="https://emanueleferonato.com/2026/01/28/greedy-rectangle-merging-turning-binary-grids-into-simple-geometry-javascript-example/">Greedy Rectangle Merging: Turning Binary Grids into Simple Geometry – JavaScript example</a></p>
<p><a href="https://fadamakis.com/you-probably-dont-need-usecallback-here-7e22d54fe7c0">You probably don’t need <code>useCallback</code> here</a></p>
<p><a href="https://github.com/vercel-labs/agent-browser">agent-browser</a> - Browser automation CLI for AI agents</p>
<p><a href="https://github.com/puffinsoft/syntux">syntux</a> - Generative UIs for the web.</p>
<p><a href="https://dev.to/playfulprogramming/javascript-frameworks-heading-into-2026-2hel">JavaScript Frameworks - Heading into 2026</a></p>
<p><a href="https://frontendmasters.com/blog/the-browser-hates-surprises/">The Browser Hates Surprises</a></p>
<p><a href="https://www.matuzo.at/blog/2026/text-scaling-meta-tag">A new meta tag for respecting text scaling on mobile</a></p>
<p><a href="https://www.phpied.com/measuring-svg-rendering-time/">Measuring SVG rendering time</a></p>
<p><a href="https://www.sanity.io/blog/the-logo-soup-problem">The logo soup problem (and how to solve it)</a></p>
<p><a href="https://www.developerway.com/posts/debugging-with-ai">Debugging with AI: Can It Replace an Experienced Developer?</a></p>
<h2>⚒️ Tools</h2>
<p><a href="https://npmx.dev/">npmx</a> — A Faster, More Informative npm Registry Browser — A new high-performance interface for exploring packages from the official npm registry. Search is quick and accurate, and package pages (for example, axios) surface richer metadata and insights at a glance. It’s not meant to replace the official registry, but it makes the default npmjs.com browsing experience feel dated. The built-in package comparison feature is especially useful.</p>
<p><a href="https://rari.build/">Rari</a> – Rust-powered React framework</p>
<p><a href="https://github.com/macaly/almostnode">almostnode</a> — Run a Node.js Environment in the Browser — An experimental project that brings a Node.js (v20) runtime directly into the browser, including basic npm package support. It’s still early and not production-ready, but the concept is intriguing and the live demo on the homepage shows promising potential.</p>
<p><a href="https://github.com/affaan-m/everything-claude-code">Everything Claude Code</a> - The complete collection of Claude Code configs from an Anthropic hackathon winner.</p>
<p><a href="https://github.com/karam-ajaj/atlas">Atlas - Network Infrastructure Visualizer</a> - Open-source tool for network discovery, visualization, and monitoring. Built with Go, FastAPI, and React, supports Docker host scanning.</p>
<p><a href="https://github.com/jeffijoe/awilix">Awilix</a> - Extremely powerful Inversion of Control (IoC) container for Node.JS</p>
<p><a href="https://github.com/moscajs/aedes">Aedes</a> - Barebone MQTT broker that can run on any stream server, the node way</p>
<p><a href="https://github.com/antfu/broz">broz</a> - A simple, frameless browser for screenshots</p>
<p><a href="https://github.com/shaka-project/shaka-player">Shaka Player</a> - JavaScript player library / DASH & HLS client / MSE-EME player</p>
<p><a href="https://www.svg.studio/">SVG Studio</a> — A Browser-Based SVG Editing Tool</p>
<p><a href="https://baseline-status-for-video.css-weekly.com/">Baseline Status for Video</a> - An Easy Way to Show Baseline Support in Videos. A small, practical utility for quickly displaying Baseline support status in video content. It helps creators visually communicate browser feature support and compatibility without building custom overlays or graphics.</p>
<p><a href="https://www.promptefy.online/">Promptefy</a> - Prompt-Driven Video Generator with Gemini</p>
<h2>📚 Libs</h2>
<p><a href="https://github.com/ShaneIsrael/fireshare">Fireshare</a> - Self host your media and share with unique links</p>
<p><a href="https://github.com/fleetbase/fleetbase">Fleetbase</a> - Modular logistics and supply chain operating system (LSOS)</p>
<p><a href="https://github.com/adesignl/Peek">Peek</a> - Light Weight "Headroom Style" scroll intent library that hides the site header on scroll down and shows on scroll up</p>
<p><a href="https://openai.github.io/chatkit-js/">ChatKit</a> — A Framework for Building AI Chat Experiences — A developer framework for adding polished, AI-powered chat interfaces to applications with minimal setup. It provides ready-made building blocks for conversational features and orchestration, helping teams ship advanced chat functionality quickly without rebuilding common patterns from scratch.</p>
<p><a href="https://github.com/image-js/image-js">image-js</a> - Image processing and manipulation in JavaScript</p>
<h2>⌚ Releases</h2>
<p><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/">TypeScript 6.0</a> has entered beta. This release isn’t about flashy new features — it focuses on simplifying and cleaning up <code>tsconfig</code> settings. Think of it as a transitional version designed to smooth the path toward the future Go-based “native” TypeScript 7 compiler.</p>
<ul>
<li><strong>Improved type inference for functions without <code>this</code></strong>: TypeScript now treats functions that don’t actually use <code>this</code> as less context-sensitive, improving inference in more cases.</li>
<li><strong>Support for subpath imports starting with <code>#/</code></strong>: You can now use imports like <code>import x from "#/module"</code> in supported environments, simplifying internal package aliasing.</li>
<li><strong>New <code>--stableTypeOrdering</code> flag</strong>:…Choosing the Right State Strategy in Reacthttps://jsdev.space/react-state-management/https://jsdev.space/react-state-management/Learn how to choose and use state management tools in React, including Context, Redux, Zustand, Jotai, MobX, and Valtio.Mon, 19 Jan 2026 00:00:00 GMT<p>State management in React is both a superpower and a recurring source of complexity. As applications grow, state stops being “just some useState hooks” and turns into a mix of:</p>
<ul>
<li>local UI state (inputs, dialogs, tabs),</li>
<li>shared UI state (layout, theme, auth),</li>
<li>server state (queries, caches, background refresh),</li>
<li>domain state (entities, workflows, lifecycles),</li>
<li>and performance constraints (minimizing wasted renders).</li>
</ul>
<p>There is no single perfect tool that solves every case. Instead, each approach makes different trade‑offs around <strong>subscriptions</strong>, <strong>granularity</strong>, <strong>debuggability</strong>, and <strong>team workflow</strong>.</p>
<p>This article walks through several state management strategies that React developers actually use in production today. Instead of yet another todo list, we will use more realistic examples: a <strong>market dashboard</strong>, <strong>trade lifecycle</strong>, <strong>user preferences</strong>, and <strong>reactive UIs</strong>.</p>
<p>We will look at:</p>
<ul>
<li>When local state is enough.</li>
<li>When Context helps, and when it hurts.</li>
<li>How Redux Toolkit enables event logs and workflows.</li>
<li>How Zustand provides simple global stores with selectors.</li>
<li>How Jotai models state as small atoms.</li>
<li>How MobX and Valtio embrace reactive, mutable models.</li>
<li>How to decide which tool to use for which problem.</li>
</ul>
<hr />
<h2>The Two Fundamental Problems of React State</h2>
<p>Every state management discussion eventually runs into two opposite pain points:</p>
<ol>
<li>
<p><strong>Data changes, but the UI does not update.</strong><br />
This is a <em>data flow</em> problem: React does not know what changed, or the change happened outside its subscription graph.</p>
</li>
<li>
<p><strong>UI updates even though the relevant data did not change.</strong><br />
This is a <em>subscriptions</em> problem: components subscribed to too much state or subscribe in a way that always returns new values.</p>
</li>
</ol>
<p>Every library in the React ecosystem is, in one way or another, an attempt to make these two problems manageable.</p>
<hr />
<h2>Approach 1: Local State with <code>useState</code> and <code>useReducer</code></h2>
<p>The simplest and often the most reliable approach is to keep state <strong>local</strong> to the component that needs it.</p>
<p>Local state is a great fit for:</p>
<ul>
<li>widget‑level interactions (modals, dropdowns, accordions);</li>
<li>form inputs and validation inside a single screen;</li>
<li>transient data that does not need to be shared.</li>
</ul>
<p>Consider a small <strong>sparkline chart</strong> that visualizes the last 50 price ticks of a stock. This is pure UI state; nothing else needs to know about it.</p>
<pre><code>import { useReducer, useEffect } from "react";
interface Candle {
t: number;
p: number;
}
type Action = { type: "tick"; price: number };
function chartReducer(state: Candle[], action: Action): Candle[] {
switch (action.type) {
case "tick":
return [...state.slice(-49), { t: Date.now(), p: action.price }];
default:
return state;
}
}
export function Sparkline({ feed }: { feed: () => number }) {
const [candles, dispatch] = useReducer(chartReducer, []);
useEffect(() => {
const id = setInterval(() => {
dispatch({ type: "tick", price: feed() });
}, 500);
return () => clearInterval(id);
}, [feed]);
return (
<pre style={{ fontSize: 12 }}>
{JSON.stringify(candles.slice(-5), null, 2)}
</pre>
);
}
</code></pre>
<p>React does not need a state library to manage this. The logic is:</p>
<ul>
<li>encapsulated,</li>
<li>easy to test,</li>
<li>and easy to throw away if the component is refactored.</li>
</ul>
<h3>When local state is ideal</h3>
<ul>
<li>The data does not need to be shared across distant components.</li>
<li>The component can own the entire lifecycle of that state.</li>
<li>Performance characteristics are simple and local.</li>
</ul>
<h3>When local state becomes a problem</h3>
<ul>
<li>Many siblings need the same state and start lifting it up repeatedly.</li>
<li>Different screens must coordinate around shared entities or workflows.</li>
<li>Debugging requires inspecting the evolution of state over time.</li>
</ul>
<p>As soon as state needs to be <strong>shared or observed by many components</strong>, we usually escalate beyond pure <code>useState</code>/<code>useReducer</code>.</p>
<hr />
<h2>Approach 2: React Context for Shared UI State</h2>
<p>React Context solves a specific problem: <strong>prop drilling</strong>. Instead of passing props through multiple levels, a Provider at the top of a subtree can expose state and APIs to any descendant.</p>
<p>Context is a great fit for:</p>
<ul>
<li>theme,</li>
<li>locale,</li>
<li>the current authenticated user,</li>
<li>feature flags,</li>
<li>routing metadata,</li>
<li>“ambient” UI state that rarely changes.</li>
</ul>
<p>However, Context has a sharp edge: <strong>when the Provider value reference changes, all consumers re‑render.</strong> Memoization is critical.</p>
<h3>Example: Shared Watchlist with Context</h3>
<p>Imagine a market dashboard where multiple components show and modify a user’s stock watchlist.</p>
<pre><code>import {
createContext,
useContext,
useCallback,
useMemo,
useState,
ReactNode,
} from "react";
interface WatchlistContextShape {
symbols: string[];
add: (symbol: string) => void;
remove: (symbol: string) => void;
}
const WatchlistContext = createContext<WatchlistContextShape | null>(null);
export function WatchlistProvider({ children }: { children: ReactNode }) {
const [symbols, setSymbols] = useState<string[]>(["AAPL", "MSFT"]);
const add = useCallback(
(sym: string) =>
setSymbols(prev => (prev.includes(sym) ? prev : [...prev, sym])),
[],
);
const remove = useCallback(
(sym: string) => setSymbols(prev => prev.filter(s => s !== sym)),
[],
);
const value = useMemo(
() => ({ symbols, add, remove }),
[symbols, add, remove],
);
return (
<WatchlistContext.Provider value={value}>
{children}
</WatchlistContext.Provider>
);
}
export function useWatchlist() {
const ctx = useContext(WatchlistContext);
if (!ctx) throw new Error("WatchlistProvider is missing");
return ctx;
}
</code></pre>
<p>Any descendant can call <code>useWatchlist()</code> to read or update the watchlist. For state that changes rarely, this works very well.</p>
<h3>Pros of Context</h3>
<ul>
<li>No external dependencies.</li>
<li>Perfect for ambient state (theme, locale, auth, feature flags).</li>
<li>Natural lifetime scoping: unmounting a Provider resets state.</li>
</ul>
<h3>Cons of Context</h3>
<ul>
<li>All consumers re‑render whenever <code>value</code> changes (unless you split Providers).</li>
<li>No built‑in selectors or fine‑grained subscriptions.</li>
<li>No action log, no time travel, and limited debugging visibility.</li>
</ul>
<p>Context is powerful but should not be the default for <strong>high‑frequency</strong> or <strong>large shared</strong> state. For those cases, other tools are better suited.</p>
<hr />
<h2>Approach 3: Redux Toolkit for Workflow‑Oriented State</h2>
<p>Redux was designed around a very specific philosophy:</p>
<blockquote>
<p>State follows actions. Every change is an event in a log.</p>
</blockquote>
<p>In its modern form (Redux Toolkit + React‑Redux), Redux is best used when:</p>
<ul>
<li>there are clear business events with semantic meaning,</li>
<li>debugging requires inspecting <em>how</em> and <em>when</em> state changed,</li>
<li>multiple teams need a shared, predictable architecture,</li>
<li>reproducibility and time‑travel debugging are important.</li>
</ul>
<p>Think of a <strong>trade order lifecycle</strong> in a trading system:</p>
<ul>
<li>draft → submit…How to Add Months to a Date in JavaScripthttps://jsdev.space/howto/add-months-to-date-js/https://jsdev.space/howto/add-months-to-date-js/Learn reliable ways to add months to a JavaScript Date, accounting for month lengths, edge cases, and timezone differences using native APIs and date-fns.Tue, 13 Jan 2026 00:00:00 GMT<p>Working with dates in JavaScript looks simple until you need real calendar arithmetic. Adding months is a subtle operation because month lengths vary, timezones affect results, and end-of-month behavior is not always obvious.</p>
<p>Before diving in, if you also work with timezones, you may benefit from this related article:</p>
<p>Previous related guide (timezone handling):<br />
<a href="https://jsdev.space/howto/timezones-date-fns/">Timezone-Safe Development with date-fns and date-fns-tz
</a></p>
<h2>Why Adding Months Is Not Just Adding 30 Days</h2>
<p>Some developers assume that one month equals 30 days:</p>
<pre><code>const date = new Date("2024-01-31");
const result = new Date(date.getTime() + 30 * 24 * 60 * 60 * 1000);
console.log(result.toISOString());
</code></pre>
<p>This does not guarantee an accurate month shift. For January 31, this often produces a date in early March instead of the end of February.</p>
<h2>Adding Months Using Native JavaScript</h2>
<p>The native approach uses <code>setMonth</code>:</p>
<pre><code>const date = new Date("2024-01-31");
const result = new Date(date);
result.setMonth(result.getMonth() + 1);
console.log(result.toISOString());
</code></pre>
<p>However, this may result in an overflow. January 31 plus one month produces a date in March because February has fewer days.</p>
<h3>Fixing End-of-Month Behavior</h3>
<p>If your domain needs end-of-month semantics (billing, subscriptions), you can enforce it:</p>
<pre><code>function addMonthsEndSafe(date, months) {
const d = new Date(date);
const day = d.getDate();
d.setMonth(d.getMonth() + months);
if (d.getDate() < day) {
d.setDate(0);
}
return d;
}
console.log(addMonthsEndSafe(new Date("2024-01-31"), 1));
</code></pre>
<p>This correctly yields <code>2024-02-29</code> when applicable.</p>
<h2>Adding Months Using date-fns</h2>
<p>The <code>date-fns</code> library provides <code>addMonths</code> for clean edge-case handling:</p>
<pre><code>import { addMonths } from "date-fns";
console.log(addMonths(new Date("2024-01-31"), 1));
// → 2024-02-29
</code></pre>
<p>Subtracting months:</p>
<pre><code>import { subMonths } from "date-fns";
console.log(subMonths(new Date("2024-05-20"), 3));
// → 2024-02-20
</code></pre>
<h2>Handling Timezones</h2>
<p>If your application involves user timezones, use <code>date-fns-tz</code> for formatting:</p>
<pre><code>import { addMonths } from "date-fns";
import { utcToZonedTime, format } from "date-fns-tz";
const utc = addMonths(new Date("2026-01-12T15:00:00.000Z"), 1);
const zone = "America/New_York";
const local = utcToZonedTime(utc, zone);
console.log(format(local, "yyyy-MM-dd HH:mmXXX"));
</code></pre>
<p>This helps avoid DST-related formatting issues.</p>
<h2>Comparison Summary</h2>
<table>
<thead>
<tr>
<th>Method</th>
<th>End-of-month safe</th>
<th>DST aware</th>
<th>Recommended</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>+30 days</code></td>
<td>No</td>
<td>No</td>
<td>Never</td>
</tr>
<tr>
<td><code>setMonth</code></td>
<td>Partial</td>
<td>Yes</td>
<td>Good</td>
</tr>
<tr>
<td>Custom safe function</td>
<td>Yes</td>
<td>Yes</td>
<td>Billing scenarios</td>
</tr>
<tr>
<td><code>date-fns addMonths</code></td>
<td>Yes</td>
<td>Yes</td>
<td>Most applications</td>
</tr>
</tbody>
</table>
<h2>Choosing the Right Strategy</h2>
<table>
<thead>
<tr>
<th>Use Case</th>
<th>Approach</th>
</tr>
</thead>
<tbody>
<tr>
<td>Billing</td>
<td>custom EOM logic</td>
</tr>
<tr>
<td>UI calendars</td>
<td>date-fns <code>addMonths</code></td>
</tr>
<tr>
<td>Internal UTC timestamps</td>
<td>native <code>setMonth</code></td>
</tr>
<tr>
<td>Finance</td>
<td>custom logic + UTC</td>
</tr>
</tbody>
</table>
<h2>Summary</h2>
<p>Adding months is not just arithmetic. Calendar correctness requires accounting for month length differences, handling overflows safely, respecting user timezones when needed, and choosing the right approach for the domain.</p>
<p><code>date-fns</code> simplifies most real-world scenarios, while custom logic may be necessary for financial systems and subscription billing.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/array-flatten/">Mastering Array Flattening in JavaScript</a></li>
<li><a href="/howto/binary-operations-js/">Mastering Advanced Binary Operations in JavaScript</a></li>
<li><a href="/howto/binding-js/">Mastering Early and Late Binding in TypeScript & JavaScript</a></li>
</ul>
How to Convert a String to a Date in JavaScripthttps://jsdev.space/howto/string-to-date-js/https://jsdev.space/howto/string-to-date-js/Learn how to convert date strings into JavaScript Date objects safely, avoid parsing pitfalls, and validate user input.Tue, 13 Jan 2026 00:00:00 GMT<p>Parsing dates from strings in JavaScript can be tricky due to locale differences, ambiguous formats, and browser inconsistencies. For example:</p>
<ul>
<li><code>"01/02/2026"</code> could mean January 2nd or February 1st depending on locale,</li>
<li><code>"2026-01-12"</code> parses reliably but may be interpreted in local timezone,</li>
<li><code>"2026-01-12 10:00"</code> is parsed differently by different engines,</li>
<li><code>new Date(string)</code> handles formats inconsistently.</li>
</ul>
<p>To avoid these pitfalls, developers should take control over parsing rules rather than relying on automatic behavior.</p>
<p>Before moving on, if timezones are also part of your workflow, this related article might be useful:</p>
<p>Related guide (timezone handling):
<a href="https://jsdev.space/howto/timezones-date-fns/">Timezone-Safe Development with date-fns and date-fns-tz</a></p>
<h2>Built‑in Parsing: Pitfalls</h2>
<p>Avoid relying on native parsing for non-ISO strings:</p>
<pre><code>new Date("2026-01-12 10:00"); // ambiguous, local parsing rules
new Date("01/12/2026"); // locale-dependent
</code></pre>
<p>Better:</p>
<pre><code>new Date("2026-01-12T10:00:00Z"); // explicit UTC
</code></pre>
<p>Or using a parsing library like <code>date-fns</code> to enforce expected formats.</p>
<h2>Using date-fns for Safe Parsing</h2>
<p><code>date-fns</code> enables explicit parsing patterns:</p>
<pre><code>import { parse, isValid } from "date-fns";
const input = "12.01.2026";
const pattern = "dd.MM.yyyy";
const reference = new Date();
const result = parse(input, pattern, reference);
if (isValid(result)) {
console.log(result.toISOString());
}
</code></pre>
<p>This prevents locale ambiguity and ensures reproducibility.</p>
<h2>React Form Parsing Demo (Practical Example)</h2>
<p>For more advanced form handling patterns, see<br />
<a href="https://jsdev.space/react-form-primitives/"><strong>React Form Primitives:</strong></a></p>
<p>The following example demonstrates how to parse a date string entered by a user inside a React controlled form, validate it, and show a formatted output.</p>
<pre><code>import React, { useState } from "react";
import { parse, isValid, format } from "date-fns";
type SupportedFormat = "yyyy-MM-dd" | "dd.MM.yyyy" | "MM/dd/yyyy";
const formatExamples: Record<SupportedFormat, string> = {
"yyyy-MM-dd": "2026-01-12",
"dd.MM.yyyy": "12.01.2026",
"MM/dd/yyyy": "01/12/2026",
};
export function DateStringForm() {
const [raw, setRaw] = useState("");
const [pattern, setPattern] = useState<SupportedFormat>("yyyy-MM-dd");
const [parsed, setParsed] = useState<Date | null>(null);
const [error, setError] = useState<string | null>(null);
const handleSubmit: React.FormEventHandler = (e) => {
e.preventDefault();
const trimmed = raw.trim();
if (!trimmed) {
setParsed(null);
setError("Please enter a date string.");
return;
}
const reference = new Date();
const result = parse(trimmed, pattern, reference);
if (!isValid(result)) {
setParsed(null);
setError(
\`Cannot parse "\${trimmed}" as \${pattern}. Example: \${formatExamples[pattern]}\`
);
return;
}
setParsed(result);
setError(null);
};
return (
<div style={{ maxWidth: 480, margin: "2rem auto", fontFamily: "system-ui" }}>
<h2>String to Date parsing demo</h2>
<p style={{ marginBottom: "1rem" }}>
This example shows how to convert a date string into a JavaScript{" "}
<code>Date</code> object using controlled form inputs and{" "}
<code>date-fns</code>. For more advanced form patterns, see{" "}
<a
href="https://jsdev.space/react-form-primitives/"
target="_blank"
rel="noreferrer"
>
React Form Primitives
</a>
.
</p>
<form onSubmit={handleSubmit} style={{ display: "grid", gap: "0.75rem" }}>
<label style={{ display: "grid", gap: "0.25rem" }}>
<span>Date string</span>
<input
type="text"
value={raw}
onChange={(e) => setRaw(e.target.value)}
placeholder={formatExamples[pattern]}
style={{
padding: "0.5rem 0.75rem",
borderRadius: 6,
border: "1px solid #ccc",
fontFamily: "inherit",
}}
/>
</label>
<label style={{ display: "grid", gap: "0.25rem" }}>
<span>Expected format</span>
<select
value={pattern}
onChange={(e) => setPattern(e.target.value as SupportedFormat)}
style={{
padding: "0.5rem 0.75rem",
borderRadius: 6,
border: "1px solid #ccc",
fontFamily: "inherit",
}}
>
<option value="yyyy-MM-dd">yyyy-MM-dd (2026-01-12)</option>
<option value="dd.MM.yyyy">dd.MM.yyyy (12.01.2026)</option>
<option value="MM/dd/yyyy">MM/dd/yyyy (01/12/2026)</option>
</select>
</label>
<button
type="submit"
style={{
marginTop: "0.5rem",
padding: "0.5rem 0.75rem",
borderRadius: 6,
border: "none",
background: "#111827",
color: "white",
cursor: "pointer",
fontFamily: "inherit",
}}
>
Parse date
</button>
</form>
<div style={{ marginTop: "1rem", fontSize: 14 }}>
{error && (
<p style={{ color: "#b91c1c", margin: 0 }}>
{error}
</p>
)}
{parsed && !error && (
<div
style={{
marginTop: "0.75rem",
padding: "0.75rem",
borderRadius: 6,
background: "#f9fafb",
border: "1px solid #e5e7eb",
}}
>
<div>
<strong>Parsed Date object:</strong>
</div>
<div>
<code>{parsed.toString()}</code>
</div>
<div style={{ marginTop: "0.5rem" }}>
<strong>ISO (UTC):</strong>{" "}
<code>{parsed.toISOString()}</code>
</div>
<div style={{ marginTop: "0.25rem" }}>
<strong>Formatted (yyyy-MM-dd):</strong>{" "}
<code>{format(parsed, "yyyy-MM-dd")}</code>
</div>
</div>
)}
</div>
</div>
);
}
</code></pre>
<h2>Summary</h2>
<p>Key takeaways:</p>
<ul>
<li>Native <code>Date</code> parsing is inconsistent for non-ISO formats,</li>
<li>Using explicit parsing rules prevents locale ambiguity,</li>
<li>React integration is straightforward with controlled inputs,</li>
<li><code>date-fns</code> improves reliability and validation.</li>
</ul>
<p>This updated MDX now includes both conceptual guidance and a practical UI demo.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/add-months-to-date-js/">Practical Month Arithmetic and Calendar Logic in JavaScript</a></li>
<li><a href="/howto/array-flatten/">Mastering Array Flattening in JavaScript</a></li>
<li><a href="/howto/binary-operations-js/">Mastering Advanced Binary Operations in JavaScript</a></li>
</ul>
How to Handle Timezones with date-fns and date-fns-tzhttps://jsdev.space/howto/timezones-date-fns/https://jsdev.space/howto/timezones-date-fns/Learn how to manage timezones, UTC conversions, and scheduling logic using date-fns, date-fns-tz, and PostgreSQL in real-world applications.Tue, 13 Jan 2026 00:00:00 GMT<p><strong>Why Timezones Matter</strong></p>
<p>Working with dates is deceptively complex. Different regions have local timezone offsets, daylight saving rules (DST), and historical changes.</p>
<p>Almost every backend stores timestamps in <strong>UTC</strong>, while frontends must show them in <strong>local time</strong>. Mistakes lead to wrong displayed times, off-by-one-day bugs, incorrect scheduling, and misaligned business rules.</p>
<p>Instead of building logic manually, this article uses <code>date-fns</code> and <code>date-fns-tz</code>, two lightweight libraries designed for timezone-safe manipulation.</p>
<h2>Installation</h2>
<pre><code>npm install date-fns date-fns-tz
</code></pre>
<h2>Basic Concepts</h2>
<ol>
<li>
<p><strong>UTC is the universal storage format</strong><br />
Example: <code>"2024-10-11T10:30:00.000Z"</code></p>
</li>
<li>
<p><strong>Local time is a presentation layer</strong><br />
UI must render times in the user’s timezone.</p>
</li>
<li>
<p><strong><code>date-fns-tz</code> gives explicit control</strong><br />
Prevents “magic conversion bugs”.</p>
</li>
</ol>
<h2>Parsing a UTC Timestamp into Local Time</h2>
<pre><code>import { format } from "date-fns";
import { utcToZonedTime } from "date-fns-tz";
const iso = "2026-01-12T15:00:00.000Z";
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const date = utcToZonedTime(iso, timezone);
console.log(format(date, "yyyy-MM-dd HH:mm:ssXXX"));
</code></pre>
<h2>Converting Local Time to UTC</h2>
<pre><code>import { zonedTimeToUtc } from "date-fns-tz";
const userInput = "2026-01-12 18:30:00";
const timezone = "Europe/Berlin";
const utc = zonedTimeToUtc(userInput, timezone);
console.log(utc.toISOString());
</code></pre>
<h2>Formatting With Timezones</h2>
<pre><code>import { format, formatISO } from "date-fns";
import { utcToZonedTime } from "date-fns-tz";
const iso = "2026-07-01T12:00:00Z";
const zone = "America/New_York";
const local = utcToZonedTime(iso, zone);
console.log(format(local, "PPpp"));
console.log(formatISO(local));
</code></pre>
<h2>Handling Daylight Saving Time (DST)</h2>
<pre><code>const winter = "2026-01-10T12:00:00Z";
const summer = "2026-07-10T12:00:00Z";
const zone = "America/New_York";
console.log(format(utcToZonedTime(winter, zone), "HH:mmXXX"));
console.log(format(utcToZonedTime(summer, zone), "HH:mmXXX"));
</code></pre>
<h2>Comparing Dates Across Timezones</h2>
<pre><code>import { getTime } from "date-fns";
const same = getTime(new Date(a)) === getTime(new Date(b));
</code></pre>
<h2>Scheduling Use Case</h2>
<pre><code>import { zonedTimeToUtc } from "date-fns-tz";
function schedule(userLocalDateTime, userZone) {
const utc = zonedTimeToUtc(userLocalDateTime, userZone);
return utc.toISOString();
}
</code></pre>
<p>For rendering:</p>
<pre><code>import { utcToZonedTime } from "date-fns-tz";
function present(utcIso, userZone) {
return utcToZonedTime(utcIso, userZone);
}
</code></pre>
<h2>Browser Timezones</h2>
<pre><code>const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
</code></pre>
<h2>Common Pitfalls</h2>
<p>Wrong:</p>
<pre><code>new Date("2026-01-12 10:00");
</code></pre>
<p>Correct:</p>
<pre><code>new Date("2026-01-12T10:00:00Z");
</code></pre>
<p>or convert using <code>zonedTimeToUtc</code>.</p>
<h2>When to Use Which Function</h2>
<table>
<thead>
<tr>
<th>Task</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>UTC → local</td>
<td><code>utcToZonedTime(utc, zone)</code></td>
</tr>
<tr>
<td>Local → UTC</td>
<td><code>zonedTimeToUtc(local, zone)</code></td>
</tr>
<tr>
<td>Format</td>
<td><code>format(date, pattern)</code></td>
</tr>
<tr>
<td>Store</td>
<td><code>date.toISOString()</code></td>
</tr>
</tbody>
</table>
<h2>Summary</h2>
<p>Working with timezones is not simple subtraction. It requires:</p>
<ul>
<li>consistent UTC storage,</li>
<li>correct local presentation,</li>
<li>DST-aware conversions,</li>
<li>predictable comparisons.</li>
</ul>
<p><code>date-fns</code> and <code>date-fns-tz</code> help avoid most timezone-related pitfalls while staying lightweight.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/add-months-to-date-js/">Practical Month Arithmetic and Calendar Logic in JavaScript</a></li>
<li><a href="/howto/array-flatten/">Mastering Array Flattening in JavaScript</a></li>
<li><a href="/howto/binary-operations-js/">Mastering Advanced Binary Operations in JavaScript</a></li>
</ul>
Howto Build Reactive Declarative UI in Vanilla JavaScripthttps://jsdev.space/howto/reactive-vanilla-js/https://jsdev.space/howto/reactive-vanilla-js/A practical experiment using vanilla JavaScript, Web APIs, and Proxy-based state to create reactive, declarative UI without frameworks.Mon, 12 Jan 2026 00:00:00 GMT<p>Modern UI frameworks offer abstraction layers that make user interfaces declarative and reactive. However, the web platform itself exposes primitives that can be composed to achieve similar patterns without introducing a dedicated UI library. This article demonstrates an experimental approach for creating a reactive, declarative UI flow using only <strong>vanilla JavaScript</strong>, <strong>Web APIs</strong>, and <strong>Proxy-based state tracking</strong>.</p>
<p>The purpose of the experiment is to examine how far native capabilities can be pushed without framework-level abstractions and to illustrate architectural benefits of declarative behavior in UI code: improved clarity, maintainability, and reduced coupling.</p>
<hr />
<h2>The Target Behavior</h2>
<p>The experiment focuses on a practical business scenario:</p>
<blockquote>
<p>Display a modal dialog that performs periodic polling of an API endpoint. The dialog should remain open until a specific condition is met, then resolve or reject accordingly.</p>
</blockquote>
<p>The modal dynamically:</p>
<ul>
<li>mounts itself into the DOM</li>
<li>starts and manages a polling process</li>
<li>exposes reactive internal state</li>
<li>updates based on the polling result</li>
<li>closes automatically when finished</li>
<li>provides optional developer controls</li>
</ul>
<p>The primary requirement is that <strong>consumer code defines what should happen, not how to wire it</strong>.</p>
<hr />
<h2>Declarative Usage Example</h2>
<p>Example invocation:</p>
<pre><code>uiModalEngine.showPollingDialog({
endpoint: `${getServiceBaseUrl()}/process/wait_for/confirmation`,
requestPayload: () => ({
taskId: currentTask.id,
mode: "rapid",
includeAudit: true,
}),
requestOptions: {
method: "POST",
headers: { "Content-Type": "application/json" },
},
shouldContinue: (response) => response.ok && response.pending === true,
intervalMs: 1000,
buildContent: (mountNode) => {
const contentBlock = uiModalEngine.createContentBlock({
title: "Waiting for confirmation...",
description: "This dialog will close automatically once the operation completes.",
})
mountNode.appendChild(contentBlock)
},
onResolved: ({ dialogNode, response }) => {
metrics.track("operation_confirmed")
dialogNode.remove()
},
onRejected: ({ dialogNode, error }) => {
logger.error("operation_polling_failed", error)
dialogNode.remove()
},
devToolsEnabled: false,
})
</code></pre>
<h3>Declarative Takeaways</h3>
<table>
<thead>
<tr>
<th>Concern</th>
<th>Ownership</th>
</tr>
</thead>
<tbody>
<tr>
<td>UI behavior</td>
<td>Declarative configuration</td>
</tr>
<tr>
<td>UI rendering</td>
<td>Modal orchestrator</td>
</tr>
<tr>
<td>DOM structure</td>
<td>DOM utility layer</td>
</tr>
<tr>
<td>polling logic</td>
<td>polling helper</td>
</tr>
<tr>
<td>reactive state</td>
<td>Proxy-based tracker</td>
</tr>
</tbody>
</table>
<p>No framework is involved, yet responsibilities remain clearly segmented.</p>
<hr />
<h2>Core Building Block: DOM Utility Layer</h2>
<p>To keep high-level code focused on behavior, DOM creation is delegated to a lightweight utility:</p>
<pre><code>class DomToolkit {
constructor(doc) {
this.doc = doc
}
static getInstance(doc) {
if (!DomToolkit.instance) DomToolkit.instance = new DomToolkit(doc)
return DomToolkit.instance
}
createElement({ tag, classes, id, attrs = {}, styles = {}, html }) {
const el = this.doc.createElement(tag)
if (id) el.id = id
if (typeof classes === "string") el.classList.add(classes)
if (Array.isArray(classes)) classes.forEach(c => el.classList.add(c))
Object.entries(attrs).forEach(([k, v]) => el.setAttribute(k, v))
Object.entries(styles).forEach(([k, v]) => el.style[k] = v)
if (html != null) el.innerHTML = html
return el
}
}
const domToolkit = DomToolkit.getInstance(document)
</code></pre>
<p>This removes boilerplate from business logic and centralizes standard element configuration.</p>
<hr />
<h2>Reactive State via Proxy</h2>
<p>Next, the experiment introduces deep reactive state using the native <code>Proxy</code> object. This allows mutations at arbitrary depth to be observed without requiring explicit setters.</p>
<pre><code>class DeepStateProxy {
constructor(target, { onSet, onDelete } = {}) {
this.onSet = onSet
this.onDelete = onDelete
return this.wrap(target, [])
}
wrap(node, path) {
if (!node || typeof node !== "object") return node
const handler = {
set: (target, key, value) => {
const fullPath = [...path, key]
target[key] = this.wrap(value, fullPath)
this.onSet?.(value, fullPath)
return true
},
deleteProperty: (target, key) => {
if (!(key in target)) return false
const fullPath = [...path, key]
delete target[key]
this.onDelete?.(fullPath)
return true
},
}
Object.keys(node).forEach(k => {
node[k] = this.wrap(node[k], [...path, k])
})
return new Proxy(node, handler)
}
}
</code></pre>
<p>Usage example:</p>
<pre><code>const state = new DeepStateProxy({
attempts: 0,
lastResponse: null,
}, {
onSet: (value, path) => console.debug("state changed:", path.join("."), value),
})
</code></pre>
<p>This approach:</p>
<p>enables deep mutation tracking<br />
does not require libraries<br />
keeps state as plain objects<br />
keeps consumer code minimal</p>
<hr />
<h2>Polling Logic as a Reusable Abstraction</h2>
<p>To isolate asynchronous logic:</p>
<pre><code>async function runPolling({ task, shouldStop, intervalMs }) {
while (true) {
const result = await task()
if (shouldStop(result)) return result
await new Promise(res => setTimeout(res, intervalMs))
}
}
</code></pre>
<p>Isolating polling enables:</p>
<ul>
<li>testability (polling logic has no DOM dependencies)</li>
<li>readability (consumer describes behavior declaratively)</li>
<li>reusability (polling can be embedded into other flows)</li>
</ul>
<hr />
<h2>Modal Orchestrator</h2>
<p>The orchestrator integrates DOM utilities, polling, and reactive state into a coherent unit:</p>
<pre><code>ModalOrchestrator.prototype.showPollingDialog = function (cfg) {
const {
endpoint, requestPayload, requestOptions,
shouldContinue, intervalMs,
buildContent, onResolved, onRejected,
devToolsEnabled = false,
} = cfg
const dialogNode = this.createDialogShell({ buildContent })
document.body.appendChild(dialogNode)
const state = new DeepStateProxy({
attempts: 0,
polling: true,
aborted: false,
lastResponse: { ok: false },
}, {
onSet: (value, path) => {
if (devToolsEnabled) console.debug("state:", path.join("."), value)
},
onDelete: () => { throw new Error("state mutation violation") },
})
state.polling = true
runPolling({
task: async () => {
const payload = requestPayload()
const res = await fetch(endpoint, { ...requestOptions, body: JSON.stringify(payload) })
.then(r => r.json())
.catch(err => ({ ok: false, error: err.message, errored: true }))
if (!shouldContinue(res) && !res.errored) state.polling = false
else state.attempts++
state.lastResponse = res
return res
},
shouldStop: () => !state.polling,
intervalMs,
})
.then(res => onResolved?.({ dialogNode, response: res }))
.catch(err => onRejected?.({ dialogNode, error: err }))
}
</code></pre>
<p>Note the absence of framework-specific concepts such as:</p>
<ul>
<li>components</li>
<li>hooks</li>
<li>virtual DOM</li>
<li>stores</li>
</ul>
<p>Yet the intent remains clear and maintainable.</p>
<hr />
<h2>Observations & Takeaways</h2>
<p>Key architectural observations include:</p>
<ol>
<li>
<p><strong>Declarative descriptions scale better than imperative wiring</strong><br />
The consumer code reads…Meet Ripple: The Elegant TypeScript UI Frameworkhttps://jsdev.space/meet-ripple/https://jsdev.space/meet-ripple/Ripple is a compiler-first TypeScript UI framework for building fast, clean, reactive applications with minimal boilerplate and optimal performance.Sun, 11 Jan 2026 00:00:00 GMT<p><a href="https://www.ripplejs.com/">Ripple</a> is a compiler-first TypeScript UI framework for building fast, clean, reactive applications with minimal boilerplate and optimal performance.</p>
<h2>Why the Frontend World Needs Ripple in 2026</h2>
<p>Front-end development has reached an unusual point in its history:<br />
writing code is easy — maintaining it is hard.</p>
<p>AI accelerated code output, but did not solve code <strong>quality</strong>, <strong>consistency</strong>, or <strong>review overhead</strong>.</p>
<p>Traditional frameworks are powerful but often come with:</p>
<ul>
<li>verbose state handling</li>
<li>over-rendering components</li>
<li>heavy abstraction layers</li>
<li>confusing refs/signals/hooks</li>
<li>bloated bundle sizes</li>
</ul>
<p>Ripple was designed for this moment. It prioritizes simplicity, clarity, and reactivity.</p>
<blockquote>
<p><em>“Code should read like it does.”</em></p>
</blockquote>
<hr />
<h2>What is Ripple?</h2>
<p>Ripple is a <strong>compiler-first, fine-grained reactive UI framework</strong> with:</p>
<ul>
<li>TypeScript-first components</li>
<li>reactive variables with <code>track()</code> + <code>@</code></li>
<li>no Virtual DOM</li>
<li>automatic dependency tracking</li>
<li>inline control flow</li>
<li>scoped CSS</li>
</ul>
<hr />
<h2>Ripple’s Design Goals</h2>
<h3>1. Compiler Before Runtime</h3>
<p>The compiler performs:</p>
<ul>
<li>DOM dependency analysis</li>
<li>dead CSS removal</li>
<li>scoped styling</li>
<li>code transformation</li>
</ul>
<h3>2. Reactive by Default</h3>
<pre><code>let count = track(0);
<button onClick={() => @count++}>{@count}</button>
</code></pre>
<p>No <code>useState</code>, <code>ref()</code>, <code>.value</code>, <code>$:</code>, or signals.</p>
<h3>3. Low Cognitive Load</h3>
<p>Less to memorize. Business logic remains obvious.</p>
<h3>4. Granular DOM Updates</h3>
<p>Only updated nodes mutate — not whole components.</p>
<hr />
<h2>Getting Started</h2>
<p>Initialize a new project:</p>
<pre><code>npx create-ripple-app ripple-todo-app
</code></pre>
<p>Move inside:</p>
<pre><code>cd ripple-todo-app
</code></pre>
<p>If integrating manually:</p>
<pre><code>npm install ripple ripple-compiler ripple-dom
</code></pre>
<h3>Scripts</h3>
<pre><code>npm run dev
npm run build
npm run preview
</code></pre>
<h3>Folder Structure</h3>
<pre><code>my-ripple-app/
├─ src/
│ ├─ App.ripple
│ ├─ index.tsx
│ └─ components/
├─ public/
├─ ripple.config.ts
├─ tsconfig.json
├─ package.json
</code></pre>
<h3>Verify Setup</h3>
<pre><code>component App() {
<h1>{"Hello Ripple"}</h1>
}
</code></pre>
<p>If it renders, you're ready.</p>
<hr />
<h2>Ripple in 2 Minutes: Core Syntax</h2>
<h3>Reactive Variables</h3>
<pre><code>let count = track(0);
</code></pre>
<h3>Read + Write</h3>
<pre><code><button onClick={() => @count++}>{@count}</button>
</code></pre>
<h3>Reactive Collections</h3>
<pre><code>const todos = #[];
const user = #{ name: "Tom" };
</code></pre>
<h3>Components</h3>
<pre><code>component Greeting({ name }) {
<h1>{"Hello "}{name}</h1>;
}
</code></pre>
<p>Inline Control Flow</p>
<pre><code>for (const item of items) {
<Item data={item}/>
}
</code></pre>
<h2>Productivity Advantages</h2>
<p>Ripple reduces maintenance cost by:</p>
<ul>
<li>fewer primitives</li>
<li>direct reactivity</li>
<li>compiler constraints</li>
<li>minimal boilerplate</li>
<li>tiny runtime</li>
</ul>
<hr />
<h2>Building a Real Demo: Todo List</h2>
<p>We’ll demonstrate Ripple’s power with a fully reactive Todo List.</p>
<h3><code>TodoInput</code> Component</h3>
<pre><code>import { track } from "ripple";
component TodoInput({ onAdd }) {
let text = track("");
function submit() {
const v = @text.trim();
if (v) {
onAdd(v);
@text = "";
}
}
<div class="input">
<input
placeholder="Add a task..."
value={@text}
onInput={(e) => @text = e.target.value}
onKeyDown={(e) => { if (e.key === "Enter") submit(); }}
/>
<button onClick={submit}>{"Add"}</button>
</div>
}
</code></pre>
<h3><code>TodoItem</code> Component</h3>
<pre><code>component TodoItem({ todo, onToggle, onDelete }) {
<li>
<input type="checkbox" checked={todo.completed} onChange={onToggle} />
<span class={todo.completed ? "done" : ""}>{todo.text}</span>
<button onClick={onDelete}>{"×"}</button>
</li>
}
</code></pre>
<h3><code>App</code> Component</h3>
<pre><code>export component App() {
const todos = #[];
function add(text) {
todos.push(#{ id: Date.now(), text, completed: false });
}
function toggle(t) {
t.completed = !t.completed;
}
function remove(id) {
const idx = todos.findIndex(t => t.id === id);
if (idx >= 0) todos.splice(idx, 1);
}
const remaining = () => todos.filter(t => !t.completed).length;
<div class="app">
<h1>{"Todo List"}</h1>
<TodoInput onAdd={add} />
<ul>
for (const t of todos) {
<TodoItem
todo={t}
onToggle={() => toggle(t)}
onDelete={() => remove(t.id)}
/>
}
</ul>
<p>{todos.length}{" total / "}{remaining()}{" remaining"}</p>
</div>
}
</code></pre>
<h2>Framework Comparison</h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Ripple</th>
<th>React</th>
<th>Vue 3</th>
<th>Svelte</th>
</tr>
</thead>
<tbody>
<tr>
<td>State model</td>
<td><code>track()</code> + <code>@</code></td>
<td>Hooks</td>
<td><code>ref()</code> / reactive</td>
<td>Stores</td>
</tr>
<tr>
<td>DOM updates</td>
<td>Fine-grained</td>
<td>VDOM diff</td>
<td>VDOM diff</td>
<td>Compile</td>
</tr>
<tr>
<td>Boilerplate</td>
<td>Very low</td>
<td>High</td>
<td>Medium</td>
<td>Low</td>
</tr>
<tr>
<td>CSS</td>
<td>Scoped</td>
<td>Modules</td>
<td>SFC Scoped</td>
<td>Scoped</td>
</tr>
<tr>
<td>AI-friendly</td>
<td>High</td>
<td>Medium</td>
<td>Medium</td>
<td>High</td>
</tr>
<tr>
<td>Runtime size</td>
<td>Small</td>
<td>Large</td>
<td>Medium</td>
<td>Tiny</td>
</tr>
</tbody>
</table>
<h2>Who Should Use Ripple?</h2>
<p>Ripple is ideal for:</p>
<ul>
<li>AI-assisted codebases</li>
<li>dashboards & realtime UIs</li>
<li>enterprise maintainability</li>
<li>mobile/web hybrid UIs</li>
<li>developers who dislike overengineering</li>
</ul>
<hr />
<h2>Official Links</h2>
<ul>
<li><a href="https://www.ripplejs.com">Website</a></li>
<li><a href="https://github.com/Ripple-TS/ripple">GitHub</a></li>
</ul>
<hr />
<h2>Final Thoughts</h2>
<p>If React gave us JSX, Vue gave us SFCs, and Svelte gave us compilation, Ripple asks:</p>
<blockquote>
<p><em>“What if UI could be reactive without ceremony?”</em></p>
</blockquote>
<p>And it answers convincingly.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/bun-workflows/">Mastering Bun for Maximum Developer Productivity</a></li>
<li><a href="/complete-monorepo-guide/">Mastering Modern Monorepo Development with pnpm, Workspaces</a></li>
<li><a href="/files-dirs-nodejs/">Get All Files and Folders in Node.js Directories</a></li>
</ul>
Clean React Architecture for Sustainable Front-End Developmenthttps://jsdev.space/maintainable-react-code/https://jsdev.space/maintainable-react-code/Learn practical techniques for writing cleaner and more maintainable React code. Covers component boundaries, utilities, conditions, data access, naming, and more.Fri, 09 Jan 2026 00:00:00 GMT<p>React projects tend to live much longer than developers expect. Features evolve, products pivot, team members change, and soon a “quick MVP” becomes a multi-year codebase. In that reality, the winning mindset isn’t “ship the UI fast”, but organize UI so future developers can ship fast too.</p>
<p>Clean code in React is not about pedantic rules or academic purity. It's about:</p>
<ul>
<li>predictable component structure</li>
<li>transparent data flow</li>
<li>minimal hidden assumptions</li>
<li>separation of concerns</li>
<li>reusable behavior and presentation layers</li>
</ul>
<p>In this extended guide, we’ll explore real-world patterns that keep React components clean, readable, and maintainable at scale — with improved examples, new entities, and modern TypeScript-friendly patterns.</p>
<h2>1. Extracting List Rendering Into Dedicated Components</h2>
<p>A common early smell: components that mix business logic, screen-level state, and large .map() rendering blocks.</p>
<h3>Overloaded Component</h3>
<pre><code>export function ProjectTabsPanel(props) {
const {
projectsArray,
selectedProjectId,
onProjectSelect,
filtersArray,
selectedFilterId,
onFilterSelect,
} = props;
const hasProjects = projectsArray.length > 0;
const hasFilters = filtersArray.length > 0;
if (!hasProjects && !hasFilters) {
return null;
}
return (
<div className="panel">
{hasProjects && (
<div className="project-section">
{projectsArray.map((item) => {
const isChosen = item.id === selectedProjectId;
return (
<button
key={item.id}
className={isChosen ? "btn active" : "btn"}
onClick={() => onProjectSelect(item.id)}
>
{item.label}
</button>
);
})}
</div>
)}
{hasFilters && (
<div className="filter-section">
{/* Filter rendering */}
</div>
)}
</div>
);
}
</code></pre>
<h3>Refactored List Component</h3>
<pre><code>type ProjectListProps = {
items: Array<{ id: string; label: string }>;
activeId: string | null;
onSelect: (id: string) => void;
};
function ProjectTabsList({ items, activeId, onSelect }: ProjectListProps) {
return (
<>
{items.map((entry) => {
const isActive = entry.id === activeId;
return (
<button
key={entry.id}
className={isActive ? "btn active" : "btn"}
onClick={() => onSelect(entry.id)}
>
{entry.label}
</button>
);
})}
</>
);
}
</code></pre>
<h2>2. Moving Helper Functions Outside of Components</h2>
<pre><code>export function TimestampText({ value }) {
const normalize = (ts: string) =>
new Date(ts).toLocaleString("en-US", { dateStyle: "medium" });
return <span>{normalize(value)}</span>;
}
</code></pre>
<h3>Better Version</h3>
<pre><code>export const US_DATE_FORMAT = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
});
export function formatTimestamp(input: string): string {
return US_DATE_FORMAT.format(new Date(input));
}
</code></pre>
<pre><code>export function TimestampText({ value }: { value: string }) {
return <span>{formatTimestamp(value)}</span>;
}
</code></pre>
<h2>3. Destructuring Props Explicitly</h2>
<pre><code>function ProfileCard({ fullName, years }: { fullName: string; years: number }) {
return (
<>
<p>{fullName}</p>
<p>{years}</p>
</>
);
}
</code></pre>
<h2>4. Extracting Complex Conditions Into Named Constants</h2>
<pre><code>useEffect(() => {
const shouldIgnoreScroll =
firstLoad && messages.length === 0 && newArrived;
if (shouldIgnoreScroll) return;
scrollDown();
}, [firstLoad, messages.length, newArrived]);
</code></pre>
<h2>5. Collapsing Deep Property Access</h2>
<pre><code>function StatsValue({ record }: { record: any }) {
const stats = record?.details?.stats;
const value = stats?.value ?? "N/A";
return <div>{value}</div>;
}
</code></pre>
<h2>6. Avoiding Magic Numbers</h2>
<pre><code>const BONUS_SCORE_THRESHOLD = 200;
const BONUS_MULTIPLIER = 1.1;
</code></pre>
<h2>Conclusion</h2>
<p>Writing clean React code is about creating systems that survive time and team growth. By separating responsibilities, extracting logic, naming conditions, and removing magic values, you make code easier to maintain, onboard, and refactor. Clean code reduces cognitive load and speeds up development — today and years from now.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/10-custom-react-hooks/">10 Must-Know Custom React Hooks for Your Projects</a></li>
<li><a href="/25-react-tips/">25 React Tips to Boost Performance and Quality</a></li>
<li><a href="/chakra-ui-guide/">Build React UIs Faster with Chakra UI</a></li>
</ul>
Friday Links #33: Modern JavaScript Picks & Highlightshttps://jsdev.space/friday/friday-33/https://jsdev.space/friday/friday-33/Stay current with JavaScript trends, tools, and releases. This edition highlights new libraries, ecosystem updates, and useful resources for modern web developers.Fri, 09 Jan 2026 00:00:00 GMT<p>JavaScript moves fast — and staying up to date can feel overwhelming. Each week we curate the most relevant updates, new libraries, and ecosystem changes so you don’t have to dig through 50 tabs and timelines. From framework releases to niche tooling improvements, this collection is designed to give developers a quick pulse check on what matters right now.</p>
<p>Welcome to edition #33 of Friday Links — your weekly window into the ever-evolving JavaScript ecosystem.</p>
<p><img src="./images/friday-33.png" alt="Friday Links #33" /></p>
<h2>OpenAI Launches ChatGPT Health</h2>
<p><img src="./images/ChatGPT-Health.png" alt="ChatGPT Health" title="ChatGPT Health" /></p>
<p>OpenAI has introduced ChatGPT Health, a dedicated section inside ChatGPT focused entirely on personal health. It’s more than a themed chat — users can discuss symptoms, interpret lab results, track metrics over time, and get clear explanations of medical terms.</p>
<p>A key feature is integration with health and fitness services. Users can connect Apple Health, MyFitnessPal, and similar apps so the AI can analyze sleep, activity, nutrition, and wellness trends. In the U.S., it can also sync with electronic health records for reviewing test results and medical history.</p>
<p>Privacy is a major emphasis: ChatGPT Health uses separate infrastructure with layered encryption, and its data is not used to train base models or mixed with regular chats by default.</p>
<h2>📜 Articles & Tutorials</h2>
<p><a href="https://courses.csail.mit.edu/6.042/spring18/mcs.pdf">Mathematics for Computer Science</a></p>
<p><a href="https://webkit.org/blog/17660/introducing-css-grid-lanes/">Introducing CSS Grid Lanes</a></p>
<p><a href="https://www.htmhell.dev/adventcalendar/2025/27/">Replacing JS with just HTML</a></p>
<p><a href="https://blog.logrocket.com/react2shell-exploit/">React2Shell exploit: What happened and lessons learned</a></p>
<p><a href="https://devblogs.microsoft.com/devops/github-copilot-for-azure-boards/">Azure Boards integration with GitHub Copilot</a></p>
<p><a href="https://www.stefanjudis.com/today-i-learned/load-env-files-in-node-js-scripts/">Automatically load .env files in Node.js scripts</a></p>
<p><a href="https://krasimirtsonev.com/blog/article/streaming-json-in-just-200-lines-of-javascript">Streaming JSON in just 200 lines of JavaScript</a></p>
<p><a href="https://openwebf.com/en/blog/announcing-webf">Introducing WebF Beta: Bring JavaScript and the Web dev to Flutter</a></p>
<p><a href="https://github.com/vadimdemedes/ink">Ink 6.6</a> — a library for building CLI apps with React, used by Claude Code, Gemini CLI, and others.</p>
<p><a href="https://www.digitalocean.com/community/tutorials/olmo-3-allen-ai-open-source-llm">Olmo 3: Fully Open-Source LLM from AI2 (Models, Data, & Code)</a></p>
<p><a href="https://railsdesigner.com/dialog-turboframe/">Use native dialog with Turbo (and no extra JavaScript)</a></p>
<p><a href="https://www.datadoghq.com/blog/datadog-database-research/">How microservice architectures have shaped the usage of database technologies</a></p>
<p><a href="https://marmelab.com/blog/2025/12/04/typescript-type-as-a-programming-language.html">TypeScript Types as a Programming Language</a></p>
<h2>⚒️ Tools</h2>
<p>A new free open-source service called <a href="https://github.com/stamparm/maltrail">Maltrail</a> has been released for analyzing inbound and outbound network traffic and detecting malware. The project can:</p>
<ul>
<li>detect malicious domains, URLs, and IP addresses</li>
<li>identify harmful HTTP User-Agent strings</li>
<li>spot modern attack tools on workstations</li>
<li>provide strong network security without complex setup — installable in one click</li>
<li>help reveal viruses, miners, and other unwanted network-active software</li>
</ul>
<p><a href="https://npmgraph.js.org/">npmgraph</a> is a web-based tool that visualizes npm package dependencies. You can enter one or more package names (or upload a <code>package.json</code>) to see how their dependency graphs intersect. It also supports coloring packages by different metrics (like number of maintainers) and exporting the result as an SVG.</p>
<p><a href="https://www.usebruno.com/">Bruno</a> - Bruno is a fully local and Git-native solution to accelerate and secure API work and collaboration</p>
<p><a href="https://github.com/cloudcommunity/Free-Certifications">Free Certifications</a> - A curated list of free courses with certifications.</p>
<p><a href="https://github.com/max-sixty/worktrunk">worktrunk</a> - A CLI tool to manage multiple worktrees in Git repositories.</p>
<p><a href="https://github.com/gibbok/typescript-book">The Concise TypeScript Book</a> - A concise and practical guide to TypeScript, covering essential concepts and features.</p>
<p><a href="https://github.com/VibiumDev/vibium">Vibium</a> - Browser automation for AI agents and humans.</p>
<h2>📚 Libs</h2>
<p><a href="https://github.com/C4illin/ConvertX">ConvertX</a> - Self-hosted online file converter. Supports 1000+ formats.</p>
<p><a href="https://fabricjs.com/">Fabric.js 7</a> - is a JavaScript library for working with the HTML5 canvas. It runs in both browsers and Node (via node-canvas) and provides an object model for canvas elements along with SVG-to-canvas and canvas-to-SVG conversion. The project also offers many demos with full source code.</p>
<p><a href="https://github.com/parallax/jsPDF">jsPDF</a> - Client-side JavaScript PDF generation for everyone.</p>
<p><a href="https://github.com/jrouwe/JoltPhysics.js">JoltPhysics.js</a> - A JavaScript/WebAssembly port of the Jolt Physics engine for real-time physics simulation in web applications.</p>
<p><a href="https://github.com/martijnversluis/ChordSheetJS">ChordSheetJS</a> - A JavaScript library for parsing and formatting chords and chord sheets</p>
<p><a href="https://github.com/playcanvas/model-viewer">PlayCanvas Model Viewer</a> - A JavaScript library for displaying 3D models in the browser using WebGL and WebXR.</p>
<p><a href="https://github.com/recharts/recharts">recharts</a> - A composable charting library built on React components and D3.js.</p>
<p><a href="https://github.com/nats-io/nats.js">nats.js</a> - JavaScript client for Node.js, Bun, Deno and browser for NATS, the cloud native messaging system</p>
<p><a href="https://github.com/LironEr/bundlemon">bundlemon</a> - A tool to monitor and enforce bundle size budgets for JavaScript projects.</p>
<p><a href="https://github.com/Lulzx/tinypdf">tinypdf</a> - A lightweight PDF manipulation library for Node.js and the browser.</p>
<p><a href="https://github.com/BlueprintLabIO/markdown-ui">Markdown UI</a> - An open standard for rendering interactive widgets in plain Markdown.</p>
<h2>⌚ Releases</h2>
<p><a href="https://pnpm.io/blog/releases/10.27">pnpm 10.27 Released</a></p>
<p><a href="https://github.com/prisma/prisma/releases/tag/7.2.0">Prisma 7.2</a></p>
<p><a href="https://github.com/denoland/deno/releases/tag/v2.6.4">Deno 2.6.4</a></p>
<p><a href="https://github.com/sindresorhus/file-type">file-type 21.2</a> - Detect the file type of a file, stream, or data</p>
<p><a href="https://github.com/taoqf/node-html-parser">Fast HTML Parser 7.0.2 Released</a></p>
<p><a href="https://middy.js.org/docs/upgrade/6-7/">Middy 7.0</a> brings middleware to AWS Lambda for Node.js, now with Durable Functions support.</p>
<p><a href="https://github.com/vercel/nft">Node File Trace 1.2</a> - determines the minimal set of files required for an app to execute.</p>
<p><a href="https://orange-orm.io/">Orange ORM 4.8</a> — an Object-Relational Mapper for Node, Bun, and Deno.</p>
<p><a href="https://github.com/yamadashy/repomix/releases/tag/v1.11.0">Repomix 1.11</a> — package an entire repository into a single, LLM-friendly file.</p>
<p><a href="https://github.com/bdeitte/hot-shots">hot-shots 12.0 / 12.1</a> — a Node.js client for statsd, DogStatsD, and Telegraf.</p>
<p><a href="https://github.com/color-js/color.js/releases/tag/v…Building Maintainable React Forms With Primitiveshttps://jsdev.space/react-form-primitives/https://jsdev.space/react-form-primitives/Learn how to replace messy custom React forms with a structured, reusable system based on primitives and field components for consistency and scalability.Fri, 02 Jan 2026 00:00:00 GMT<h2>Introduction</h2>
<p>“Not again… another form.”</p>
<p>Working with forms in React is one of the most common frontend tasks — and one of the easiest places to accumulate technical debt. Before worrying about architecture, every developer has to understand the basics: how form events work, how values flow from inputs, and how React and TypeScript model those interactions.</p>
<p>If you’re not fully confident in that layer, the guide - <a href="https://jsdev.space/react-form-events-guide/">React Form Events & TypeScript</a> does an excellent job of breaking down <code>onChange</code>, <code>onSubmit</code>, <code>onBlur</code>, <code>onFocus</code>, and their TypeScript typings in a clear, practical way.</p>
<p>However, even with a solid understanding of form events, another problem quickly appears. Knowing <em>how</em> forms work doesn’t automatically tell you <em>how to structure them</em> once your application grows. A simple form with a few inputs is easy to manage, but as soon as you have 10–20 fields, shared validation rules, server errors, and multiple screens using the same inputs, ad‑hoc solutions stop scaling.</p>
<p>This article focuses on that next layer of complexity. Instead of discussing events and handlers, we’ll look at how to design a <strong>predictable, reusable form system</strong> built on four ideas:</p>
<ol>
<li><strong>UI primitives</strong> — small, “dumb” components responsible only for appearance.</li>
<li><strong>A Cell wrapper</strong> — a single place for labels, hints, and error messages.</li>
<li><strong>Field components</strong> — thin adapters between React Hook Form and your UI.</li>
<li><strong>Schemas</strong> — one source of truth for validation and TypeScript types.</li>
</ol>
<p>Together, these layers help turn forms from a recurring source of chaos into a solved, scalable part of your UI architecture.</p>
<h2>The Scaling Problem: “Toy Form” vs “Product Form”</h2>
<p>A typical first form is built with local state and a submit handler. It works — until it doesn’t.</p>
<pre><code>import * as React from "react";
export function SignInToy() {
const [email, setEmail] = React.useState("");
const [secret, setSecret] = React.useState("");
const [issues, setIssues] = React.useState<{ email?: string; secret?: string }>(
{}
);
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const next: typeof issues = {};
if (!email.includes("@")) next.email = "Please enter a valid email.";
if (secret.trim().length < 6) next.secret = "Minimum 6 characters.";
setIssues(next);
if (Object.keys(next).length === 0) {
// send request...
}
};
return (
<form onSubmit={onSubmit}>
<div>
<label>Email</label>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
{issues.email && <div>{issues.email}</div>}
</div>
<div>
<label>Password</label>
<input
type="password"
value={secret}
onChange={(e) => setSecret(e.target.value)}
/>
{issues.secret && <div>{issues.secret}</div>}
</div>
<button type="submit">Sign in</button>
</form>
);
}
</code></pre>
<p>Now imagine this pattern multiplied across a large application. More fields mean more state, more branching validation logic, and more duplicated markup. At that point, reviewing, testing, and maintaining forms becomes disproportionately expensive.</p>
<h2>Form Libraries Help Logic, Not UI</h2>
<p>Libraries like React Hook Form reduce boilerplate and improve performance, but they mainly solve <strong>state management and validation wiring</strong>. They don’t define how your fields should look, how errors are rendered, or how consistency is enforced across the UI.</p>
<p>That missing layer is where most form complexity actually lives.</p>
<h2>The Structured Approach</h2>
<p>The solution is to separate concerns clearly:</p>
<ul>
<li><strong>Primitives</strong> define how inputs look and behave at the lowest level.</li>
<li><strong>Cell</strong> defines how a “field” is presented: label, hint, error.</li>
<li><strong>Field components</strong> connect form state to UI.</li>
<li><strong>Schemas</strong> define validation and typing in one place.</li>
</ul>
<p>This separation keeps each piece small and understandable — and makes the whole system easier to scale.</p>
<h2>Step 1: UI Primitives</h2>
<p>Primitives are intentionally boring. They know nothing about forms or validation.</p>
<pre><code>import * as React from "react";
export const TextInput = React.forwardRef<
HTMLInputElement,
React.InputHTMLAttributes<HTMLInputElement>
>(function TextInput(props, ref) {
return <input ref={ref} {...props} className="input" />;
});
</code></pre>
<p>Because primitives are generic, you can reuse them outside forms — in filters, search bars, or settings panels.</p>
<h2>Step 2: The Cell Wrapper</h2>
<p>The <code>Cell</code> component standardizes layout and error display.</p>
<pre><code>import * as React from "react";
type CellProps = {
label?: string;
hint?: React.ReactNode;
error?: string;
children: React.ReactNode;
};
export function Cell({ label, hint, error, children }: CellProps) {
return (
<div className="cell">
{label && <label className="cell__label">{label}</label>}
<div className="cell__control">{children}</div>
{hint && <div className="cell__hint">{hint}</div>}
{error && <div className="cell__error">{error}</div>}
</div>
);
}
</code></pre>
<p>All visual consistency flows through this component.</p>
<h2>Step 3: Field Components</h2>
<p>Field components glue React Hook Form to your UI.</p>
<pre><code>import { useFormContext } from "react-hook-form";
export function TextField({ name, label, ...props }) {
const {
register,
formState: { errors },
} = useFormContext();
return (
<Cell label={label} error={errors[name]?.message}>
<TextInput {...register(name)} {...props} />
</Cell>
);
}
</code></pre>
<p>Each field type lives in its own small file, making behavior explicit and predictable.</p>
<h2>Step 4: Schemas and Validation</h2>
<p>Schemas (for example, with Zod) give you a single source of truth.</p>
<pre><code>import { z } from "zod";
export const ProfileSchema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(6, "Minimum 6 characters"),
});
export type ProfileValues = z.infer<typeof ProfileSchema>;
</code></pre>
<p>Validation rules and TypeScript types stay in sync.</p>
<h2>Step 5: Assembling a Form</h2>
<pre><code>import { FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
export function ProfileForm() {
const methods = useForm({
resolver: zodResolver(ProfileSchema),
});
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(console.log)}>
<TextField name="email" label="Email" />
<TextField name="password" label="Password" type="password" />
<button type="submit">Save</button>
</form>
</FormProvider>
);
}
</code></pre>
<p>The form reads like intent, not implementation details.</p>
<h2>Conclusion</h2>
<p>Understanding React form events is the foundation — but structure is what makes forms scale. By layering primitives, a shared Cell wrapper, and small field components on top of a form library, you get consistency, reuse, and clarity without sacrificing flexibility.</p>
<p>Once this system is in place, forms stop being a special problem and become just another predictable part of your UI.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/10-custo…How to Use the never Type for Real-World Error Handlinghttps://jsdev.space/howto/typescript-never-error-handling-guide/https://jsdev.space/howto/typescript-never-error-handling-guide/Learn how to use TypeScript’s never type to model impossible states, enforce exhaustive checks, and catch missing error cases before they reach production.Tue, 25 Nov 2025 00:00:00 GMT<p>The <code>never</code> type in TypeScript is often explained as “a function that never returns”.
While technically correct, this explanation is rarely useful in real-world codebases.</p>
<p>In practice, <code>never</code> becomes powerful when you want the compiler to fail loudly as your
application evolves — especially when handling errors, exhaustive checks, and states
that should be impossible but tend to appear over time.</p>
<p>In this guide, we’ll look at how <code>never</code> behaves in production code, why it is easy
to misuse, and how it helps catch entire classes of bugs before they reach runtime.</p>
<hr />
<h2>What <code>never</code> Really Means</h2>
<p>The TypeScript type system models types as <strong>sets of values</strong>:</p>
<ul>
<li><code>number</code> is the set of all possible numbers</li>
<li><code>boolean</code> is the set <code>{true, false}</code></li>
<li>string literal types like <code>"ready"</code> are a set with a single element</li>
<li><code>never</code> is the <strong>empty set</strong></li>
</ul>
<p>Because <code>never</code> has <em>no possible values</em>, no actual value can ever belong to it. That’s why the following function is valid:</p>
<pre><code>export function triggerFailure(reason: string): never {
throw new Error(reason);
}
</code></pre>
<p>TypeScript ensures that functions returning <code>never</code> cannot complete normally.</p>
<pre><code>const outcome = triggerFailure("Unexpected state!");
// ^? never
</code></pre>
<p><code>outcome</code> has type <code>never</code>, because TypeScript knows the function cannot return.</p>
<hr />
<h2>Exhaustive error handling</h2>
<pre><code>type ApiResult =
| { status: "success"; data: string }
| { status: "error"; error: Error };
function assertNever(value: never): never {
throw new Error(`Unhandled case: ${JSON.stringify(value)}`);
}
function handleResult(result: ApiResult) {
switch (result.status) {
case "success":
return result.data;
case "error":
throw result.error;
default:
return assertNever(result);
}
}
</code></pre>
<p>Here, never ensures that every possible state of ApiResult is handled.
If a new status is added later, TypeScript will immediately report an error.</p>
<p>This is where never becomes valuable — not as a theoretical type,
but as a guardrail for evolving codebases.</p>
<h2>The Common Mistake: Using <code>never</code> for Error Modeling</h2>
<p>Developers sometimes attempt this pattern:</p>
<pre><code>export function safeDivide(x: number, y: number): number | never {
if (y === 0) {
throw new Error("Division by zero!");
}
return x / y;
}
</code></pre>
<p>They expect the return type to reflect both “good” and “error” states.</p>
<p>But TypeScript evaluates:</p>
<pre><code>number | never → number
</code></pre>
<p>The empty set adds nothing to the union — <code>never</code> collapses and becomes meaningless.</p>
<p>This is why:</p>
<pre><code>const result = safeDivide(10, 0);
// result: number
</code></pre>
<p>This code successfully compiles but is misleading.<br />
The <code>never</code> type <strong>should never be used to describe an error state</strong>.</p>
<hr />
<h2>Why <code>never</code> Collapses in Unions</h2>
<p>Unions represent the <strong>set‑theoretic union</strong> of their members.</p>
<pre><code>(number-set) ∪ (empty-set) = number-set
</code></pre>
<p>This is not a TypeScript quirk — it is mathematically correct.</p>
<p>Thus:</p>
<ul>
<li><code>string | never</code> → <code>string</code></li>
<li><code>boolean | never</code> → <code>boolean</code></li>
<li><code>T | never</code> → <code>T</code></li>
</ul>
<p><code>never</code> disappears because it cannot contribute any valid value.</p>
<hr />
<h2>The Correct Use of <code>never</code>: Exhaustiveness Checking</h2>
<p><code>never</code> becomes powerful when used to model <strong>impossible states</strong>.</p>
<h3>Step 1 — Create a discriminated union</h3>
<pre><code>type CircleShape = { kind: "circle"; radius: number };
type SquareShape = { kind: "square"; side: number };
type RectShape = { kind: "rectangle"; width: number; height: number };
type ShapeEntity = CircleShape | SquareShape | RectShape;
</code></pre>
<h3>Step 2 — Write an exhaustiveness checker</h3>
<pre><code>function assertImpossible(value: never): never {
throw new Error("Encountered an impossible case: " + JSON.stringify(value));
}
</code></pre>
<h3>Step 3 — Use it inside a switch</h3>
<pre><code>export function computeArea(geom: ShapeEntity): number {
switch (geom.kind) {
case "circle":
return Math.PI * geom.radius ** 2;
case "square":
return geom.side ** 2;
case "rectangle":
return geom.height * geom.width;
default:
return assertImpossible(geom);
}
}
</code></pre>
<p>If someone later updates:</p>
<pre><code>type TriangleShape = { kind: "triangle"; a: number; b: number; c: number };
type ShapeEntity = CircleShape | SquareShape | RectShape | TriangleShape;
</code></pre>
<p>The compiler immediately screams:</p>
<pre><code>Argument of type 'TriangleShape' is not assignable to parameter of type 'never'.
</code></pre>
<p>This is exactly how <code>never</code> should be used:<br />
catching missing branches<br />
preventing silent logic failures<br />
enforcing total coverage of all cases</p>
<hr />
<h2>A Better Model for Error Handling: The Result Pattern</h2>
<p>Instead of misusing <code>never</code>, model error states explicitly using a discriminated union:</p>
<pre><code>type FailureInfo = { status: "fail"; message: string };
type SuccessInfo<T> = { status: "ok"; data: T };
type ResultBox<T> = FailureInfo | SuccessInfo<T>;
</code></pre>
<p>Helper creators:</p>
<pre><code>const createFailure = (msg: string): FailureInfo => ({
status: "fail",
message: msg,
});
const createSuccess = <T>(value: T): SuccessInfo<T> => ({
status: "ok",
data: value,
});
</code></pre>
<p>A safe division using proper error modeling:</p>
<pre><code>export function robustDivide(n1: number, n2: number): ResultBox<number> {
if (n2 === 0) return createFailure("Division by zero");
return createSuccess(n1 / n2);
}
</code></pre>
<p>Usage:</p>
<pre><code>const output = robustDivide(8, 0);
if (output.status === "fail") {
console.error("Error:", output.message);
} else {
console.log("Result:", output.data);
}
</code></pre>
<p>This is explicit, predictable, and fully typed.</p>
<hr />
<h2>Wrapping Unsafe Functions: A <code>try</code>-Safe Wrapper</h2>
<p>Sometimes an existing function throws. Wrap it safely:</p>
<pre><code>export function handleSafely<Args extends unknown[], Ret>(
fn: (...p: Args) => Ret,
...inputs: Args
): ResultBox<Ret> {
try {
return createSuccess(fn(...inputs));
} catch (err: any) {
return createFailure(err?.message ?? "Unknown failure");
}
}
</code></pre>
<p>Example:</p>
<pre><code>function riskyDivide(a: number, b: number) {
if (b === 0) throw new Error("Boom!");
return a / b;
}
const checked = handleSafely(riskyDivide, 10, 0);
</code></pre>
<hr />
<h2>Converting ResultBox to a Throwing Function</h2>
<p>If needed, convert the safe result back into a throwing workflow:</p>
<pre><code>export function unwrapOrCrash<T>(supplier: () => ResultBox<T>): T {
const outcome = supplier();
if (outcome.status === "ok") return outcome.data;
throw new Error(outcome.message);
}
</code></pre>
<p>Usage:</p>
<pre><code>const finalAnswer = unwrapOrCrash(() => robustDivide(10, 2));
</code></pre>
<h2>Why <code>never</code> Often Fails in Real Projects</h2>
<p>Using <code>never</code> does not automatically make code safer.</p>
<p>Common mistakes include:</p>
<ul>
<li>assuming <code>never</code> replaces runtime validation</li>
<li>overusing it in public APIs</li>
<li>hiding real errors behind “impossible” states</li>
</ul>
<p>In production, <code>never</code> works best when combined with:</p>
<ul>
<li>discriminated …Chakra UI: A Complete Guide to Faster, Cleaner, and Accessible UIhttps://jsdev.space/chakra-ui-guide/https://jsdev.space/chakra-ui-guide/A practical deep-dive into Chakra UI v3: component styling, theming, accessibility, performance, responsive design, and modern React examples.Mon, 24 Nov 2025 00:00:00 GMT<p>In day‑to‑day frontend work, we keep solving the same problems:</p>
<ul>
<li>building consistent buttons, forms, layouts, and modals</li>
<li>making everything responsive on phones, tablets, and desktops</li>
<li>keeping colors, typography, and spacing uniform across the app</li>
<li>implementing accessibility and keyboard navigation correctly</li>
<li>fighting with ever‑growing CSS files and design drift</li>
</ul>
<p>Writing CSS from scratch for every new page quickly turns into a chore. That’s exactly the type of repetitive work <strong>Chakra UI</strong> removes from your life.</p>
<p>In this guide, we’ll look at how <strong>Chakra UI v3</strong> paired with <strong>React</strong> helps you:</p>
<ul>
<li>stop hand‑writing CSS for every component</li>
<li>keep a single source of truth for design tokens</li>
<li>ship accessible UI without memorizing every WAI‑ARIA rule</li>
<li>build responsive layouts without writing <code>@media</code> queries</li>
<li>keep performance under control with tree‑shaking and minimal styling runtime</li>
</ul>
<p>Along the way we’ll use improved code samples, renamed variables, and modern patterns.</p>
<blockquote>
<p>Official docs: <a href="https://chakra-ui.com/">chakra docs</a></p>
</blockquote>
<hr />
<h2>Why Styling Libraries Exist at All</h2>
<p>Let’s be honest: vanilla CSS is powerful, but it doesn’t scale nicely in large React apps.</p>
<p>Typical problems when you style everything manually:</p>
<h3>1. Slowness</h3>
<p>You might spend <strong>hours</strong> on details like hover states, focus rings, input errors, spacing between elements, and layout quirks.</p>
<h3>2. Visual Inconsistency</h3>
<p>You start with a clean design, but six months later:<br />
different border radii, slightly different blues, spacing that "almost" matches, and three button variations that should have been one.</p>
<h3>3. Accessibility Debt</h3>
<p>Screen readers, roles, ARIA attributes, focus management, ESC handling, keyboard navigation – all this is crucial, but hard to do correctly and consistently with plain HTML + CSS.</p>
<h3>4. Bloated CSS</h3>
<p>Legacy styles accumulate: old classes, unused helpers, and confusing overrides. Bundle size grows, and nobody wants to delete anything because "it might be used somewhere".</p>
<p><strong>Component styling libraries</strong> like Chakra UI solve these with:</p>
<ul>
<li>ready‑made building blocks (Button, Stack, Modal, Menu, etc.)</li>
<li>theme tokens instead of magic values</li>
<li>accessibility first approach</li>
<li>built‑in responsiveness</li>
</ul>
<p>Chakra takes this approach and makes it very ergonomic for React.</p>
<hr />
<h2>1. Styling with Props Instead of Raw CSS</h2>
<p>In Chakra, <strong>style props</strong> are the core idea:<br />
you describe <em>what</em> the component should look like directly in JSX instead of switching between JS and CSS files.</p>
<h3>A basic button, Chakra style</h3>
<pre><code>import { Button } from "@chakra-ui/react"
export function SendMessageButton() {
return (
<Button
colorScheme="teal"
size="lg"
borderRadius="xl"
boxShadow="md"
_hover={{
boxShadow: "xl",
transform: "translateY(-1px)",
}}
>
Send message
</Button>
)
}
</code></pre>
<p><img src="./images/chakra-btn.png" alt="Chakra UI button example" /></p>
<p>This one component includes:</p>
<ul>
<li>colors</li>
<li>size</li>
<li>border radius</li>
<li>shadow</li>
<li>hover state</li>
</ul>
<p>No separate CSS file, no BEM class names, no <code>:hover</code> selectors.</p>
<h3>Equivalent CSS for comparison</h3>
<pre><code>.primary-button {
background-color: #319795;
color: #fff;
padding: 0.75rem 1.5rem;
border-radius: 0.75rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, .1);
transition: all 0.15s ease-out;
}
.primary-button:hover {
box-shadow: 0 10px 15px rgba(0, 0, 0, .15);
transform: translateY(-1px);
}
</code></pre>
<pre><code>export function SendMessageButtonRaw() {
return <button className="primary-button">Send message</button>
}
</code></pre>
<p>Both work, but the Chakra version:</p>
<ul>
<li>keeps markup and styles in one place</li>
<li>is more discoverable (props are auto‑completed in your IDE)</li>
<li>plays nicely with dynamic values coming from state or props</li>
</ul>
<p><a href="https://chakra-ui.com/docs/styling/overview">Style props reference</a></p>
<hr />
<h2>2. A Single Theme Controls the Whole App</h2>
<p>Instead of scattering values like <code>#319795</code> and <code>1.5rem</code> across components, Chakra encourages you to put them into a <strong>theme</strong>.</p>
<p>You can extend the default theme or create your own system.</p>
<h3>Minimal theme setup with design tokens</h3>
<pre><code>import {
ChakraProvider,
createSystem,
defaultConfig,
defineConfig,
} from "@chakra-ui/react"
const designConfig = defineConfig({
theme: {
tokens: {
colors: {
accent: { value: "#6ED209" },
surface: { value: "#F8FFF2" },
},
radii: {
pill: { value: "999px" },
},
},
},
})
const designSystem = createSystem(defaultConfig, designConfig)
export function RootApp({ children }: { children: React.ReactNode }) {
return <ChakraProvider value={designSystem}>{children}</ChakraProvider>
}
</code></pre>
<p>Now you can use <code>accent</code> and <code>surface</code> anywhere:</p>
<pre><code>import { Box, Button } from "@chakra-ui/react"
export function AccentCard() {
return (
<Box bg="surface" p={6} borderRadius="lg">
<Button bg="accent" borderRadius="pill">
Accent action
</Button>
</Box>
)
}
</code></pre>
<p>Change <code>accent</code> in one place → entire app updates.</p>
<p><a href="https://chakra-ui.com/docs/theming/overview">Theming guide</a></p>
<h3>Extracting theme configuration into its own file</h3>
<p>A common pattern is to keep all theme logic in something like <code>theme/system.ts</code>:</p>
<pre><code>// theme/system.ts
import { defineConfig, createSystem, defaultConfig } from "@chakra-ui/react"
const designConfig = defineConfig({
globalCss: {
"html, body": {
margin: 0,
padding: 0,
fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
scrollBehavior: "smooth",
},
},
theme: {
tokens: {
colors: {
"ink-strong": { value: "#1A202C" },
"ink-soft": { value: "#718096" },
"paper": { value: "#F7FAFC" },
},
},
semanticTokens: {
colors: {
appBackground: {
value: {
base: "{colors.paper}",
_dark: "#1A202C",
},
},
appText: {
value: {
base: "{colors.ink-strong}",
_dark: "{colors.paper}",
},
},
},
},
},
})
export const designSystem = createSystem(defaultConfig, designConfig)
</code></pre>
<p>Then in your entry point:</p>
<pre><code>// app/providers.tsx
import { ChakraProvider } from "@chakra-ui/react"
import { designSystem } from "@/theme/system"
export function Providers({ children }: { children: React.ReactNode }) {
return <ChakraProvider value={designSystem}>{children}</ChakraProvider>
}
</code></pre>
<p>This gives you <strong>one place</strong> to tune your entire design language.</p>
<hr />
<h2>3. Accessibility Built In by Default</h2>
<p>Manually making UI accessible means dealing with:</p>
<ul>
<li><code>role</code>, <code>aria-*</code> attributes</li>
<li>focus traps for modals</li>
<li>escape handling</li>
<li>tab order for menus</li>
<li>keyboard navigation for lists and dialogs</li>
<li><code>aria-live</code> for announcements</li>
</ul>
<p>Chakra UI bakes this into its components.</p>
<p><a href="https://www.w3.org/WAI/standards-guidelines/aria/">WAI‑ARIA specs</a></p>
<h3>Accessible alert message</h3>
<pre><code>import { Alert, AlertIcon, AlertTitle, AlertDescription } from "@c…Mastering React Form Events with TypeScripthttps://jsdev.space/react-form-events-guide/https://jsdev.space/react-form-events-guide/Learn how React form events work, how to type them in TypeScript, and explore improved examples: inputs, forms, buttons, keyboard events, validation, debouncing...Mon, 24 Nov 2025 00:00:00 GMT<p>Working with forms is one of the most common tasks in React — and one of the most misunderstood. Beginners often struggle with event types, how to strongly‑type form handlers, how to extract values, and how to work with keyboard, mouse, focus, and form‑submission events correctly.</p>
<p>This improved guide explains <strong>every major React form event</strong>, adds <strong>better TypeScript examples</strong>, and focuses heavily on <strong>real‑world patterns</strong> like validation, debouncing, controlled inputs, preventing default behavior, and working with form data.</p>
<hr />
<h2>Why React Form Events Matter</h2>
<p>Any time a user:</p>
<ul>
<li>types into an input</li>
<li>clicks a button</li>
<li>submits a form</li>
<li>focuses or blurs a field</li>
<li>presses a key</li>
<li>interacts with checkboxes, selects, radios, sliders</li>
</ul>
<p>…React fires an event object you can react to.</p>
<p>TypeScript helps us <strong>ensure correctness</strong>, catch mistakes, and avoid bugs like undefined values or invalid event targets.</p>
<hr />
<h2>1. <code>onChange</code>: The Most Important Form Event</h2>
<h3>TypeScript Type:</h3>
<pre><code>React.ChangeEvent<HTMLInputElement>
</code></pre>
<p><code>onChange</code> fires whenever the value of an input changes.</p>
<h3>Improved Real‑World Example: Typing with Validation</h3>
<pre><code>import { useState } from "react";
export function UsernameField() {
const [username, setUsername] = useState("");
const [error, setError] = useState("");
const handleUsername = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setUsername(value);
if (value.length < 3) {
setError("Username must be at least 3 characters.");
} else {
setError("");
}
};
return (
<div>
<label>
Username:
<input type="text" value={username} onChange={handleUsername} />
</label>
{error && <p style={{ color: "red" }}>{error}</p>}
</div>
);
}
</code></pre>
<hr />
<h2>2. <code>onSubmit</code>: Handling Entire Form Submission</h2>
<h3>TypeScript Type:</h3>
<pre><code>React.FormEvent<HTMLFormElement>
</code></pre>
<h3>Improved Example with FormData Extraction</h3>
<pre><code>import { useState } from "react";
export function LoginForm() {
const [message, setMessage] = useState("");
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const form = e.currentTarget;
const data = new FormData(form);
const email = data.get("email") as string;
const password = data.get("password") as string;
setMessage(`Submitted: ${email} / ${password}`);
};
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" placeholder="Email" required />
<input name="password" type="password" placeholder="Password" required />
<button type="submit">Log In</button>
<p>{message}</p>
</form>
);
}
</code></pre>
<hr />
<h2>3. <code>onFocus</code>: Detecting When a User Enters a Field</h2>
<pre><code>React.FocusEvent<HTMLInputElement>
</code></pre>
<p>Example: Highlight active field</p>
<pre><code>import { useState } from "react";
export function FocusExample() {
const [focused, setFocused] = useState(false);
return (
<div>
<input
onFocus={() => setFocused(true)}
style={{ borderColor: focused ? "dodgerblue" : "#ccc" }}
/>
{focused && <p>You're typing now!</p>}
</div>
);
}
</code></pre>
<hr />
<h2>4. <code>onBlur</code>: When the User Leaves a Field</h2>
<pre><code>React.FocusEvent<HTMLInputElement>
</code></pre>
<p>Example: Email validation</p>
<pre><code>import { useState } from "react";
export function EmailField() {
const [error, setError] = useState("");
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
const email = e.target.value;
setError(email.includes("@") ? "" : "Invalid email.");
};
return (
<div>
<input type="email" onBlur={handleBlur} placeholder="Enter email" />
{error && <p style={{ color: "red" }}>{error}</p>}
</div>
);
}
</code></pre>
<hr />
<h2>5. <code>onClick</code>: Buttons & Interactions</h2>
<h3>TypeScript Type:</h3>
<pre><code>React.MouseEvent<HTMLButtonElement>
</code></pre>
<p>Example: Button loading state</p>
<pre><code>import { useState } from "react";
export function LoadingButton() {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
await new Promise(res => setTimeout(res, 1000));
setLoading(false);
};
return (
<button onClick={handleClick} disabled={loading}>
{loading ? "Loading..." : "Click me"}
</button>
);
}
</code></pre>
<hr />
<h2>6. Keyboard Events</h2>
<pre><code>React.KeyboardEvent<HTMLInputElement>
</code></pre>
<p>Example: Submit on Enter</p>
<pre><code>import { useState } from "react";
export function SearchBox() {
const [query, setQuery] = useState("");
const handleKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
alert(`Searching for: ${query}`);
}
};
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKey}
placeholder="Press Enter to search"
/>
);
}
</code></pre>
<hr />
<h2>7. Bonus: Debounced Input</h2>
<pre><code>import { useState, useEffect } from "react";
export function DebouncedInput() {
const [raw, setRaw] = useState("");
const [debounced, setDebounced] = useState("");
useEffect(() => {
const id = setTimeout(() => setDebounced(raw), 400);
return () => clearTimeout(id);
}, [raw]);
return (
<div>
<input
onChange={(e) => setRaw(e.target.value)}
placeholder="Type slowly..."
/>
<p>Debounced: {debounced}</p>
</div>
);
}
</code></pre>
<hr />
<h2>Final Thoughts</h2>
<p>You now know how to handle:</p>
<ul>
<li>change events</li>
<li>submit events</li>
<li>focus/blur</li>
<li>click events</li>
<li>keyboard events</li>
<li>validation</li>
<li>debouncing</li>
<li>extracting FormData</li>
</ul>
<p>React + TypeScript becomes much easier once you understand event types.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/10-custom-react-hooks/">10 Must-Know Custom React Hooks for Your Projects</a></li>
<li><a href="/25-react-tips/">25 React Tips to Boost Performance and Quality</a></li>
<li><a href="/chakra-ui-guide/">Build React UIs Faster with Chakra UI</a></li>
</ul>
Howto Fix the “Role Postgres Does Not Exist” Error in Dockerhttps://jsdev.space/howto/fix-postgres-role-error/https://jsdev.space/howto/fix-postgres-role-error/Learn how to fix the "role 'postgres' does not exist" error when deploying a PostgreSQL Docker container, why it happens, and the correct solutions.Mon, 17 Nov 2025 00:00:00 GMT<p>When deploying a PostgreSQL instance inside a Docker container, one of the most common startup failures is:</p>
<pre><code>psql: FATAL: role "postgres" does not exist
</code></pre>
<p>This error typically indicates that the initialization scripts did not create the default <code>postgres</code> superuser. Below we explore the causes and the correct fixes.</p>
<hr />
<h2>1. Why the Error Occurs</h2>
<h3>1.1 Your data volume already has an old database</h3>
<p>PostgreSQL only creates users when the data directory is empty.<br />
If you reuse a volume, the <code>postgres</code> role may simply not exist.</p>
<h3>1.2 You changed the default POSTGRES_USER</h3>
<pre><code>POSTGRES_USER=myuser
</code></pre>
<p>This makes <code>myuser</code> the superuser — not <code>postgres</code>.</p>
<h3>1.3 Incorrect filesystem permissions</h3>
<p>If PostgreSQL cannot write to <code>/var/lib/postgresql/data</code>, initialization may silently fail.</p>
<hr />
<h2>2. Solutions</h2>
<h3><strong>A. Delete the old volume (recommended)</strong></h3>
<pre><code>docker compose down -v
docker compose up -d
</code></pre>
<h3><strong>B. Explicitly set POSTGRES_USER</strong></h3>
<pre><code>environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret123
</code></pre>
<p>Restart:</p>
<pre><code>docker compose down
docker compose up -d
</code></pre>
<h3><strong>C. Manually create the missing role</strong></h3>
<pre><code>docker exec -it <container> bash
psql -U myuser
</code></pre>
<p>Inside PostgreSQL:</p>
<pre><code>CREATE ROLE postgres WITH SUPERUSER LOGIN PASSWORD 'changeme';
</code></pre>
<h3><strong>D. Fix permissions</strong></h3>
<pre><code>sudo chown -R 999:999 /path/to/volume
docker compose restart
</code></pre>
<hr />
<h2>3. Best Practices</h2>
<ul>
<li>Always specify <code>POSTGRES_USER</code> and <code>POSTGRES_PASSWORD</code>.</li>
<li>Prefer <em>named volumes</em> over bind mounts.</li>
<li>Add healthchecks:</li>
</ul>
<pre><code>healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
</code></pre>
<hr />
<h2>4. Conclusion</h2>
<p>This error happens because the container is using an already-initialized volume or the default superuser was changed. Resetting the volume, correcting your environment variables, or creating the role manually will resolve the problem.</p>
<p>If you want extended troubleshooting for specific platforms (Railway, Render, Fly.io, Docker Swarm), I can add that too.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/jaeger-docker-windows/">Setting Up Jaeger with Docker on Windows</a></li>
<li><a href="/howto/postgresql-docker-compose/">Setting Up Local PostgreSQL with Docker Compose</a></li>
<li><a href="/howto/add-months-to-date-js/">Practical Month Arithmetic and Calendar Logic in JavaScript</a></li>
</ul>
How to Develop Accessible Interfaces with WAI-ARIAhttps://jsdev.space/howto/wai-aria-accessible-ui/https://jsdev.space/howto/wai-aria-accessible-ui/A deep, practical guide to building accessible interfaces using WAI-ARIA roles, states, patterns, and proper keyboard support for modern web applications.Mon, 17 Nov 2025 00:00:00 GMT<p>Modern web applications have evolved into rich, desktop‑like interactive environments. Components such as modals, dropdown menus, accordions, tabs, and dynamically updated content are now standard. However, accessibility is often overlooked, leaving millions of users unable to interact with applications effectively. WAI‑ARIA provides a way to enrich custom UI components with semantic meaning and behavior for assistive technologies.</p>
<h2>What WAI‑ARIA Is</h2>
<p>WAI‑ARIA (Web Accessibility Initiative — Accessible Rich Internet Applications) is a specification created by the W3C to describe interactive interfaces to screen readers, keyboard users, and assistive devices. While HTML includes semantic elements like <code><button></code> and <code><nav></code>, developers frequently build UI using <code><div></code> or <code><span></code>, resulting in missing information for accessibility tools. ARIA fills in those gaps.</p>
<p>ARIA includes three core building blocks:</p>
<ul>
<li><strong>Roles</strong> — define the purpose of an element</li>
<li><strong>States & Properties</strong> (<code>aria-*</code>) — describe its current condition</li>
<li><strong>Live Regions</strong> — announce dynamic updates</li>
</ul>
<h2>ARIA Roles</h2>
<p>Roles communicate what an element represents.</p>
<p>Examples include:</p>
<ul>
<li><code>button</code></li>
<li><code>navigation</code></li>
<li><code>dialog</code></li>
<li><code>tab</code>, <code>tablist</code>, <code>tabpanel</code></li>
<li><code>menu</code>, <code>menuitem</code></li>
<li><code>checkbox</code>, <code>switch</code></li>
</ul>
<p>Use native HTML elements whenever possible; use ARIA only when necessary.</p>
<h2>ARIA Properties & States</h2>
<p>These attributes explain how a UI element behaves and what its current status is.</p>
<p><strong>Properties</strong></p>
<ul>
<li><code>aria-label</code></li>
<li><code>aria-labelledby</code></li>
<li><code>aria-describedby</code></li>
</ul>
<p><strong>States</strong></p>
<ul>
<li><code>aria-expanded</code></li>
<li><code>aria-selected</code></li>
<li><code>aria-checked</code></li>
<li><code>aria-disabled</code></li>
</ul>
<h2>Live Regions</h2>
<p>Live regions notify users of dynamic content updates:</p>
<ul>
<li><code>aria-live="polite"</code></li>
<li><code>aria-live="assertive"</code></li>
<li><code>aria-atomic="true"</code></li>
<li><code>aria-relevant="additions text"</code></li>
</ul>
<p>These are essential for notifications, error messages, chat updates, and async content.</p>
<h2>ARIA in SPA/React Environments</h2>
<p>Frameworks like React sometimes re-render without changing DOM nodes, which can prevent screen readers from detecting updates. Use:</p>
<ul>
<li>Live regions</li>
<li>Forced DOM updates</li>
<li>Proper <code>role</code> and <code>aria-*</code> attributes</li>
<li>Real testing with assistive tools</li>
</ul>
<h2>Keyboard Accessibility</h2>
<p>Keyboard navigation is a core WCAG requirement.</p>
<p>Essential keys:</p>
<ul>
<li><strong>Tab / Shift+Tab</strong> — move focus</li>
<li><strong>Enter / Space</strong> — activate</li>
<li><strong>Arrow keys</strong> — navigate inside composite widgets</li>
<li><strong>Escape</strong> — close dialogs</li>
<li><strong>Home / End</strong> — jump to start/end</li>
</ul>
<p>Use:</p>
<ul>
<li><code>tabindex="0"</code></li>
<li><code>tabindex="-1"</code></li>
<li><code>aria-activedescendant</code> for virtual focus</li>
</ul>
<h2>Testing Accessibility</h2>
<h3>Browser Tools</h3>
<ul>
<li>Axe DevTools</li>
<li>Lighthouse</li>
<li>WAVE</li>
</ul>
<h3>Screen Readers</h3>
<ul>
<li>NVDA</li>
<li>JAWS</li>
<li>VoiceOver</li>
</ul>
<h3>Linters</h3>
<ul>
<li><code>eslint-plugin-jsx-a11y</code></li>
</ul>
<h3>CI</h3>
<ul>
<li>Axe CI</li>
<li>Pa11y CI</li>
<li>Lighthouse CI</li>
</ul>
<h2>Example: Accessible Accordion Component</h2>
<p>Here is <strong>a completely different example</strong>, not related to the previous code and without any internal comments directed at you. It demonstrates a well‑structured, accessible accordion widget using WAI‑ARIA best practices.</p>
<h3>HTML Structure</h3>
<pre><code><div class="accordion">
<h2 id="faq-title">FAQ</h2>
<div class="accordion__item">
<button
class="accordion__trigger"
aria-expanded="false"
aria-controls="sect-1"
id="accordion-btn-1"
>
What is accessibility?
</button>
<div
class="accordion__panel"
id="sect-1"
role="region"
aria-labelledby="accordion-btn-1"
hidden
>
<p>
Accessibility ensures that interfaces can be used by people with
disabilities or assistive technologies.
</p>
</div>
</div>
<div class="accordion__item">
<button
class="accordion__trigger"
aria-expanded="false"
aria-controls="sect-2"
id="accordion-btn-2"
>
What is WAI‑ARIA?
</button>
<div
class="accordion__panel"
id="sect-2"
role="region"
aria-labelledby="accordion-btn-2"
hidden
>
<p>
WAI‑ARIA provides roles and attributes that describe UI behavior to
assistive technologies.
</p>
</div>
</div>
</div>
</code></pre>
<h3>JavaScript Logic (Brand‑New Example)</h3>
<pre><code>class AriaAccordion {
constructor(containerEl) {
this.containerEl = containerEl;
this.triggers = Array.from(
containerEl.querySelectorAll(".accordion__trigger")
);
this.triggers.forEach(btn => {
btn.addEventListener("click", () => this.toggle(btn));
btn.addEventListener("keydown", e => this.onKey(e, btn));
});
}
toggle(buttonEl) {
const isOpen = buttonEl.getAttribute("aria-expanded") === "true";
const newState = !isOpen;
buttonEl.setAttribute("aria-expanded", newState);
const panelId = buttonEl.getAttribute("aria-controls");
const panel = document.getElementById(panelId);
panel.hidden = !newState;
}
onKey(event, buttonEl) {
const { code } = event;
const index = this.triggers.indexOf(buttonEl);
if (code === "ArrowDown") {
event.preventDefault();
const next = this.triggers[index + 1] || this.triggers[0];
next.focus();
}
if (code === "ArrowUp") {
event.preventDefault();
const prev =
this.triggers[index - 1] || this.triggers[this.triggers.length - 1];
prev.focus();
}
}
}
document
.querySelectorAll(".accordion")
.forEach(acc => new AriaAccordion(acc));
</code></pre>
<h2>Conclusion</h2>
<p>ARIA is powerful, but with that power comes responsibility. Developers should:</p>
<ul>
<li>use semantic HTML whenever possible,</li>
<li>apply ARIA roles and states only when appropriate,</li>
<li>provide keyboard interaction patterns,</li>
<li>test with real assistive technologies,</li>
<li>and follow W3C ARIA Authoring Practices.</li>
</ul>
<p>By embracing these principles, you ensure your interface works smoothly for every user—regardless of device, ability, or assistive technology.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/custom-html-tag/">Mastering Custom Elements in HTML5</a></li>
<li><a href="/howto/html-output-tag/">The Overlooked Power of the HTML <code><output></code> Element</a></li>
<li><a href="/howto/wcag-compliance/">Checking Web Pages for WCAG Compliance</a></li>
</ul>
Identify If a Value Belongs to a Class in JavaScripthttps://jsdev.space/snippets/check-instance-js/https://jsdev.space/snippets/check-instance-js/Learn how to correctly check if a value is an instance of a class in JavaScript using prototype inspection, Symbol.hasInstance, and primitive mapping.Mon, 17 Nov 2025 00:00:00 GMT<h2>Introduction</h2>
<p>Instance checking in JavaScript appears simple at first—just use <code>instanceof</code>.<br />
But real-world scenarios (and tasks like <em>LeetCode 2618</em>) require correctly handling:</p>
<ul>
<li>primitive values (<code>42</code>, <code>'hi'</code>, <code>true</code>, <code>5n</code>)</li>
<li>wrapper constructors (<code>Number</code>, <code>String</code>, etc.)</li>
<li>invalid constructor inputs</li>
<li>functions as instances of <code>Function</code></li>
<li>prototype chain traversal</li>
<li>custom instance logic via <code>Symbol.hasInstance</code></li>
</ul>
<p>This snippet provides three reliable solutions, rewritten and improved for clarity, correctness, and edge-case safety.</p>
<hr />
<h2>Solution 1 — Recommended</h2>
<h3>Using <code>instanceof</code> + Primitive Mapping</h3>
<p>Works for objects and functions, with explicit special rules for built-in primitive wrappers.</p>
<pre><code>function isInstance(candidate, ctor) {
if (ctor == null || typeof ctor !== "function") return false;
if (candidate == null) return false;
if (candidate instanceof ctor) return true;
const type = typeof candidate;
if (ctor === Number) return type === "number";
if (ctor === String) return type === "string";
if (ctor === Boolean) return type === "boolean";
if (ctor === BigInt) return type === "bigint";
if (ctor === Symbol) return type === "symbol";
return false;
}
</code></pre>
<hr />
<h2>Solution 2</h2>
<h3>Manual Prototype Chain Traversal</h3>
<pre><code>function isInstanceByPrototype(value, ctor) {
if (ctor == null || typeof ctor !== "function") return false;
if (value == null) return false;
const type = typeof value;
if (ctor === Number) return type === "number";
if (ctor === String) return type === "string";
if (ctor === Boolean) return type === "boolean";
if (ctor === BigInt) return type === "bigint";
if (ctor === Symbol) return type === "symbol";
if (type !== "object" && type !== "function") return false;
let proto = Object.getPrototypeOf(value);
const target = ctor.prototype;
while (proto) {
if (proto === target) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
}
</code></pre>
<hr />
<h2>Solution 3</h2>
<h3>Using <code>Symbol.hasInstance</code></h3>
<pre><code>function isInstanceBySymbol(value, ctor) {
if (ctor == null || typeof ctor !== "function") return false;
if (value == null) return false;
const hook = ctor[Symbol.hasInstance];
if (typeof hook === "function" && hook.call(ctor, value)) {
return true;
}
const type = typeof value;
if (ctor === Number) return type === "number";
if (ctor === String) return type === "string";
if (ctor === Boolean) return type === "boolean";
if (ctor === BigInt) return type === "bigint";
if (ctor === Symbol) return type === "symbol";
return false;
}
</code></pre>
<hr />
<h2>Examples</h2>
<pre><code>class Creature {}
class Feline extends Creature {}
function Demo() {}
console.log(isInstance(new Date(), Date));
console.log(isInstance(new Feline(), Creature));
console.log(isInstance(123, Number));
console.log(isInstance("ok", String));
console.log(isInstance(true, Boolean));
console.log(isInstance(7n, BigInt));
console.log(isInstance(Symbol("x"), Symbol));
console.log(isInstance(123, Object));
console.log(isInstance(null, Object));
console.log(isInstance(undefined, Number));
console.log(isInstance(Creature, Creature));
console.log(isInstance(Demo, Function));
</code></pre>
<hr />
<h2>Related content</h2>
<ul>
<li><a href="/snippets/comprehensive-websocket-client/">WebSocket Client JavaScript Implementation</a></li>
<li><a href="/snippets/deepfreeze-js/">Deeply Freeze a Nested Object</a></li>
<li><a href="/snippets/epsg4326-epsg3857/">Convert coordinates from EPSG-4326 to EPSG-3857</a></li>
</ul>
Howto Use the Browser’s Four Built-In Multithreading APIshttps://jsdev.space/howto/browser-multithreading-apis/https://jsdev.space/howto/browser-multithreading-apis/Learn how Web Workers, Shared Workers, Service Workers, and Worklets provide real multithreading capabilities in the browser.Sun, 16 Nov 2025 00:00:00 GMT<p>Modern browsers provide four multithreading mechanisms — Web Worker, Shared Worker, Service Worker, and Worklet — each running on different browser threads. Although JavaScript itself is single‑threaded, these APIs enable parallel computation, background tasks, offline caching, and real-time rendering.</p>
<h2>1. Why Workers Enable Multithreading</h2>
<p>JavaScript runs on a single thread, but browsers use a multi-process architecture. Worker threads are isolated from the main thread, communicate via message passing, avoid UI blocking, and have thread limits.</p>
<h2>2. Overview of the Four Worker Types</h2>
<table>
<thead>
<tr>
<th>Worker Type</th>
<th>Purpose</th>
<th>Thread</th>
<th>Use Case</th>
</tr>
</thead>
<tbody>
<tr>
<td>Web Worker</td>
<td>CPU-heavy tasks</td>
<td>Worker thread pool</td>
<td>Sorting, cryptography</td>
</tr>
<tr>
<td>Shared Worker</td>
<td>Shared state across tabs</td>
<td>Shared worker thread</td>
<td>Multi-tab sync</td>
</tr>
<tr>
<td>Service Worker</td>
<td>Offline caching and request control</td>
<td>Background thread</td>
<td>PWAs, offline apps</td>
</tr>
<tr>
<td>Worklet</td>
<td>Real-time rendering</td>
<td>Rendering pipeline</td>
<td>CSS animation, audio processing</td>
</tr>
</tbody>
</table>
<h2>3. Web Worker — Heavy Computation</h2>
<pre><code>// main.js
const worker = new Worker('sort-worker.js');
worker.postMessage([5, 1, 3]);
worker.onmessage = (e) => {
console.log('Sorted:', e.data);
worker.terminate();
};
</code></pre>
<pre><code>// sort-worker.js
self.onmessage = (e) => {
const sorted = e.data.sort((a, b) => a - b);
self.postMessage(sorted);
self.close();
};
</code></pre>
<h2>4. Shared Worker — Cross-Tab State Sync</h2>
<pre><code>// sync-worker.js
const ports = [];
let state = { isLogin: false };
self.onconnect = (e) => {
const port = e.ports[0];
ports.push(port);
port.onmessage = (msg) => {
if (msg.data.type === 'LOGIN') {
state = msg.data.data;
ports.forEach(p => p.postMessage(state));
}
};
};
</code></pre>
<h2>5. Service Worker — Offline Proxy</h2>
<pre><code>// sw.js
const CACHE = 'v1';
self.addEventListener('install', (e) => {
e.waitUntil(
caches.open(CACHE).then(c => c.addAll(['/','/index.html']))
);
});
</code></pre>
<h2>6. Worklet — Rendering Pipeline Logic</h2>
<pre><code>// bounce-worklet.js
class BounceWorklet {
process(inputs, outputs) {
const t = inputs[0][0];
const y = Math.sin(t * Math.PI);
outputs[0].value = `translateY(${300 * (1 - y)}px)`;
}
}
registerAnimator('bounce', BounceWorklet);
</code></pre>
<h2>7. Combined Multi‑Worker Architecture</h2>
<p>A real-world dashboard may combine all four Worker types to achieve smooth rendering, offline use, cross‑tab sync, and fast data parsing.</p>
<h2>8. Using <code>self</code> in Workers</h2>
<p>Workers use <code>self</code> instead of <code>window</code> and cannot access the DOM. They can only perform non-UI tasks.</p>
<h2>9. Production Considerations</h2>
<p>Workers require explicit error handling, careful cleanup, same-origin script loading, and performance balancing to avoid overhead.</p>
<h2>Conclusion</h2>
<p>Each Worker type solves a different class of performance problems. Understanding how they differ enables developers to build fast, reliable, non-blocking web applications.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/add-months-to-date-js/">Practical Month Arithmetic and Calendar Logic in JavaScript</a></li>
<li><a href="/howto/array-flatten/">Mastering Array Flattening in JavaScript</a></li>
<li><a href="/howto/binary-operations-js/">Mastering Advanced Binary Operations in JavaScript</a></li>
</ul>
Making PowerShell More Comfortable: Configuring PSReadLinehttps://jsdev.space/psreadline-setup/https://jsdev.space/psreadline-setup/A practical guide to installing, updating, and configuring PSReadLine to make PowerShell faster, smarter, and more comfortable for everyday use.Fri, 14 Nov 2025 00:00:00 GMT<p>Many Windows users rely on PowerShell every day, but out of the box the experience can feel primitive: endless tapping of the Up arrow to find a command, missed typos in long paths, unreliable history search, and awkward navigation. Modern terminals on Linux or macOS offer far more convenience.</p>
<p>Fortunately, PowerShell can be upgraded with <a href="https://github.com/PowerShell/PSReadLine"><strong>PSReadLine</strong></a>, a module that enhances input handling, adds predictive suggestions, improves navigation, and turns the console into a more capable tool. Here's how to install and update PSReadLine correctly, why the built-in version may be outdated, and how to apply a full working configuration that improves everyday workflows.</p>
<p>The goal is simple: by the end, you will have a ready-to-use <code>Microsoft.PowerShell_profile.ps1</code> and a faster, more pleasant terminal experience.</p>
<h2>1. Understanding PSReadLine (and Why the Built-In Version May Not Work)</h2>
<p><img src="./images/PSReadLine.png" alt="PSReadLine" title="PSReadLine" /></p>
<p>PowerShell ships with PSReadLine built in — but the version included can be <strong>old</strong> or <strong>inconsistent across systems</strong>. This leads to multiple problems noted by users:</p>
<ul>
<li>Some systems still run <strong>PSReadLine v2.0</strong>, which does <em>not</em> support <code>-PredictionSource</code>, <code>-PredictionViewStyle</code>, or other modern options.</li>
<li>Updating the module may fail (<code>Module 'PSReadLine' was not installed</code>).</li>
<li>PowerShell 5.1 on Windows 11 may not load the updated version at all.</li>
<li>In some cases, the built-in version must be removed before updates will apply.</li>
</ul>
<p>Because of this, installing the latest version manually is often easier.</p>
<h2>2. Installing or Updating PSReadLine Correctly</h2>
<p>The most reliable installation command is:</p>
<pre><code>Install-Module -Name PSReadLine -Force -SkipPublisherCheck -AllowPrerelease
</code></pre>
<p>Explanation:</p>
<ul>
<li><code>-Force</code> — overrides old module copies</li>
<li><code>-SkipPublisherCheck</code> — fixes signature mismatches</li>
<li><code>-AllowPrerelease</code> — enables newest features</li>
<li><code>-AllowClobber</code> — sometimes required if the built-in version blocks updates</li>
</ul>
<p>If PowerShell claims the module is already installed or refuses to update, remove it:</p>
<pre><code>Remove-Module PSReadLine -Force
</code></pre>
<p>Then install again.</p>
<p>Check which versions are available:</p>
<pre><code>Get-Module PSReadLine -ListAvailable
</code></pre>
<p>If prediction options are unavailable, you are still on v2.0.</p>
<h2>3. Creating or Editing the PowerShell Profile</h2>
<p>The PowerShell profile loads custom PSReadLine settings every time a new session opens.</p>
<p>Find the profile path:</p>
<pre><code>echo $PROFILE
</code></pre>
<p>Create it if missing:</p>
<pre><code>if (!(Test-Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
notepad $PROFILE
</code></pre>
<p>Everything below goes in that file.</p>
<p><img src="./images/notepad_profile.png" alt="Notepad profile" /></p>
<h2>4. A Practical PSReadLine Configuration</h2>
<p>This setup provides:</p>
<ul>
<li>Command predictions based on history</li>
<li>Bash-style reverse search (<code>Ctrl+R</code>)</li>
<li>A structured completion menu</li>
<li>Intuitive keybindings</li>
<li>Improved color scheme</li>
<li>A quick command validator (<code>F2</code>)</li>
<li>A more modern, editor-like feel</li>
</ul>
<pre><code># PSReadLine Configuration
Import-Module PSReadLine
# Prediction and Useful UI Elements
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineOption -ShowToolTips
# Color Scheme
Set-PSReadLineOption -Colors @{
Command = 'Yellow'
Parameter = 'Green'
String = 'DarkCyan'
InlinePrediction = 'DarkGray'
}
# Enhanced Key Bindings
Set-PSReadLineKeyHandler -Key Ctrl+r -Function ReverseSearchHistory
Set-PSReadLineKeyHandler -Key Ctrl+Spacebar -Function MenuComplete
Set-PSReadLineKeyHandler -Key Tab -Function Complete
Set-PSReadLineKeyHandler -Key Ctrl+c -Function Copy
Set-PSReadLineKeyHandler -Key Ctrl+v -Function Paste
Set-PSReadLineKeyHandler -Key Ctrl+LeftArrow -Function BackwardWord
Set-PSReadLineKeyHandler -Key Ctrl+RightArrow -Function NextWord
Set-PSReadLineKeyHandler -Key Home -Function BeginningOfLine
Set-PSReadLineKeyHandler -Key End -Function EndOfLine
# Quick Command Existence Check (F2)
Set-PSReadLineKeyHandler -Key F2 -ScriptBlock {
$line = $null
$cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
$firstWord = ($line -split '\s+')[0]
if ($firstWord -and (Get-Command $firstWord -ErrorAction SilentlyContinue)) {
Write-Host " Command exists" -ForegroundColor Green -NoNewline
} elseif ($firstWord) {
Write-Host " Command NOT FOUND" -ForegroundColor Red -NoNewline
}
[Microsoft.PowerShell.PSConsoleReadLine]::RedrawLine()
}
</code></pre>
<h2>5. Testing the Improvements</h2>
<p>After restarting PowerShell, the enhancements should be easy to notice:</p>
<h3>Command Predictions</h3>
<p>Type <code>git sta</code> → see prediction like <code>git status</code>.</p>
<p><img src="./images/powershell-autocomplete.png" alt="PowerShell autocomplete" title="PowerShell autocomplete" /></p>
<h3>Reverse Search</h3>
<p>Press <strong>Ctrl+R</strong> → type part of a past command → fuzzy matches appear.</p>
<h3>Completion Menu</h3>
<p>Type <code>Get-</code> → press <strong>Ctrl+Space</strong> → view all matching commands.</p>
<p><img src="./images/Completion-Menu.png" alt="Completion Menu" title="Completion Menu" /></p>
<h3>Quick Command Check</h3>
<p>Type a typo (<code>Get-ChilItem</code>) → press <strong>F2</strong> → get immediate feedback.</p>
<h3>Editor-Like Navigation</h3>
<ul>
<li><code>Ctrl+Left/Right</code> moves by words</li>
<li><code>Home/End</code> jumps to line boundaries</li>
<li><code>Ctrl+C</code>/<code>Ctrl+V</code> behave normally</li>
</ul>
<p>These changes significantly speed up command-line workflows.</p>
<h2>6. Notes from Community Feedback</h2>
<p>User discussions highlight several important points:</p>
<h3>• The built-in PSReadLine often loads an outdated version.</h3>
<p>This explains why some users cannot access prediction features.</p>
<h3>• Installation and updating can fail.</h3>
<p>Removing the module first usually resolves the problem.</p>
<h3>• PowerShell 5.1 remains problematic on Windows 11.</h3>
<p>Users may need additional steps.</p>
<h3>• Alternatives exist.</h3>
<p>Some prefer <code>oh-my-posh</code>, <code>starship</code>, or Git Bash tools.</p>
<h3>• Even skeptics agree the upgraded experience is far better.</h3>
<p>PSReadLine dramatically reduces repetitive effort and errors.</p>
<h2>Conclusion</h2>
<p>PowerShell can be frustrating in its default state, but PSReadLine transforms it into a much more efficient environment. With prediction, intuitive navigation, improved history search, and a modern editing feel, the console becomes faster and easier to use.</p>
<p>Whether you rarely use PowerShell or rely on it heavily, this configuration acts like an integrated cheat sheet—reducing friction and boosting productivity.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/error-handling-bash/">5 Essential Error Handling Methods in Bash</a></li>
<li><a href="/10-css-tips/">Top 10 CSS Tips to Master in 2024</a></li>
<li><a href="/10-custom-react-hooks/">10 Must-Know Custom React Hooks for Your Projects</a></li>
</ul>
Howto Use Blob Objects for Efficient File Handlinghttps://jsdev.space/howto/blob-file-handling-guide/https://jsdev.space/howto/blob-file-handling-guide/Learn how to use Blob objects to handle files, stream large uploads, compress images, export data, and avoid memory leaks in modern front-end apps.Fri, 14 Nov 2025 00:00:00 GMT<p>In modern front-end apps, file handling is everywhere: image uploads, CSV exports, previews, and rich editors. But once files get big or numerous, things start to break: the UI freezes, memory usage spikes, and the browser occasionally dies.</p>
<p>This guide walks through <strong>six practical Blob techniques</strong> that help handle files efficiently and safely:</p>
<ul>
<li>Creating Blobs correctly</li>
<li>Chunking large files</li>
<li>Compressing and converting images</li>
<li>Implementing reliable file previews</li>
<li>Exporting data as downloadable files</li>
<li>Managing memory to avoid Blob URL leaks</li>
</ul>
<p>The goal: <strong>make file handling fast, stable, and production-ready.</strong></p>
<hr />
<h2>1. Creating Blob Objects Safely and Efficiently</h2>
<h3>Common pain</h3>
<p>Many developers start by manipulating giant strings or base64 data URLs, which:</p>
<ul>
<li>Duplicate data in memory</li>
<li>Blow up heap usage</li>
<li>Slow down the UI</li>
</ul>
<pre><code>// Bad: huge string + data URL = double memory usage
const hugeText = 'Very long text...'.repeat(100_000);
const dataUrl =
'data:text/plain;charset=utf-8,' + encodeURIComponent(hugeText);
</code></pre>
<h3>Use Blobs instead</h3>
<p>A Blob is a lightweight wrapper around binary data. The browser can stream it, slice it, and build URLs from it without copying the entire payload.</p>
<pre><code>/**
* Safely create a Blob from data chunks.
*/
const createBlob = (
parts: BlobPart[],
options: BlobPropertyBag = {}
): Blob => {
return new Blob(parts, {
type: options.type ?? 'text/plain',
endings: options.endings ?? 'transparent',
});
};
// Text Blob
const messageBlob = createBlob(['Hello, Blob!'], {
type: 'text/plain',
});
// JSON Blob
const userProfile = { name: 'Alex', age: 29 };
const profileBlob = createBlob([JSON.stringify(userProfile)], {
type: 'application/json',
});
// HTML Blob
const htmlSnippet = '<h1>Dynamically Generated HTML</h1>';
const htmlBlob = createBlob([htmlSnippet], { type: 'text/html' });
</code></pre>
<h3>Real-world use: “download config as file”</h3>
<pre><code>const downloadConfig = (config: unknown) => {
const serialized = JSON.stringify(config, null, 2);
const blob = new Blob([serialized], {
type: 'application/json',
});
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = 'config.json';
document.body.appendChild(anchor);
anchor.click();
anchor.remove();
URL.revokeObjectURL(url);
};
</code></pre>
<p><strong>Key ideas</strong></p>
<ul>
<li>Blobs <strong>wrap data without copying it</strong>.</li>
<li>Always set a <strong>correct MIME type</strong> (e.g., <code>application/json</code>, <code>image/jpeg</code>).</li>
<li>Use <code>URL.createObjectURL(blob)</code> to get a fast, stream-friendly URL.</li>
</ul>
<hr />
<h2>2. Chunking Large Files to Avoid Memory Explosions</h2>
<h3>Common pain</h3>
<p>Reading a 2 GB log file with <code>FileReader.readAsText()</code> in one go is a great way to:</p>
<ul>
<li>Freeze the tab</li>
<li>Hit memory limits</li>
<li>Crash the browser</li>
</ul>
<pre><code>// Bad: read entire file into memory
const processLargeFile = (file: File) => {
const reader = new FileReader();
reader.onload = (event) => {
const text = event.target?.result as string;
// Massive string in memory
handleWholeFile(text);
};
reader.readAsText(file);
};
</code></pre>
<h3>Better: process in chunks with <code>slice()</code></h3>
<pre><code>/**
* Process a file in fixed-size chunks to avoid memory pressure.
*/
const processFileInChunks = async (
file: File,
chunkSize = 1024 * 1024,
onProgress?: (info: {
current: number;
total: number;
percentage: number;
}) => void
) => {
const totalChunks = Math.ceil(file.size / chunkSize);
const results: unknown[] = [];
for (let index = 0; index < totalChunks; index++) {
const start = index * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const result = await readChunk(chunk, index);
results.push(result);
onProgress?.({
current: index + 1,
total: totalChunks,
percentage: Math.round(((index + 1) / totalChunks) * 100),
});
}
return results;
};
const readChunk = (
chunk: Blob,
index: number
): Promise<{ index: number; size: number; sample: string }> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const text = event.target?.result as string;
resolve({
index,
size: chunk.size,
sample: text.slice(0, 100),
});
};
reader.onerror = () => reject(reader.error);
reader.readAsText(chunk);
});
};
</code></pre>
<h3>Real-world use: chunked upload class</h3>
<pre><code>class ChunkedUploader {
private file: File;
private chunkSize: number;
private uploadUrl: string;
private onProgress?: (info: {
uploaded: number;
total: number;
percentage: number;
}) => void;
constructor(file: File, options: {
chunkSize?: number;
uploadUrl: string;
onProgress?: (info: { uploaded: number; total: number; percentage: number }) => void;
}) {
this.file = file;
this.chunkSize = options.chunkSize ?? 2 * 1024 * 1024; // 2MB
this.uploadUrl = options.uploadUrl;
this.onProgress = options.onProgress;
}
async upload() {
const totalChunks = Math.ceil(this.file.size / this.chunkSize);
const uploadId = this.createUploadId();
for (let index = 0; index < totalChunks; index++) {
const start = index * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
const chunk = this.file.slice(start, end);
await this.uploadChunk(chunk, index, uploadId);
this.onProgress?.({
uploaded: index + 1,
total: totalChunks,
percentage: Math.round(((index + 1) / totalChunks) * 100),
});
}
return this.mergeChunks(uploadId, totalChunks);
}
private async uploadChunk(chunk: Blob, index: number, uploadId: string) {
const body = new FormData();
body.append('chunk', chunk);
body.append('index', String(index));
body.append('uploadId', uploadId);
const response = await fetch(this.uploadUrl, { method: 'POST', body });
if (!response.ok) {
throw new Error(`Chunk ${index} upload failed`);
}
}
private async mergeChunks(uploadId: string, totalChunks: number) {
const response = await fetch('/api/merge-chunks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ uploadId, totalChunks }),
});
if (!response.ok) {
throw new Error('Failed to merge chunks');
}
return response.json();
}
private createUploadId() {
return `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
}
}
</code></pre>
<hr />
<h2>3. Image Compression and Format Conversion on the Front End</h2>
<h3>Common pain</h3>
<p>Uploading raw 8 MB photos directly from phones or cameras:</p>
<ul>
<li>Wastes bandwidth</li>
<li>Slows uploads</li>
<li>Hurts perceived performance</li>
</ul>
<pre><code>// No compression: heavy uploads, slow UX
const uploadOriginalImage = (file: File) => {
const body = new FormData();
body.append('image', file);
return fetch('/upload', { method: 'POST', body });
};
</code></pre>
<h3>Better: compress with <code><canvas></code> and Blob</h3>
<pre><code>interface CompressionOptions {
maxWidth?: number;
maxHeight?: number;
quality?: number;
outputType?: string;
}
const compressImage = (
file: File,
options: CompressionOptions = {}
): Promise<Blob> => {
const {
maxWidth = 1920,
maxHeight = 1080,
quality = 0.8,
outputType = 'image/jpeg',
} = options;
return new Promise((resolve, reject) =>…Friday Links #32: The Latest JavaScript Trends & Toolshttps://jsdev.space/friday/friday-32/https://jsdev.space/friday/friday-32/A curated roundup of the latest JavaScript trends, tools, releases, and insights from the community. Fresh updates to stay ahead in modern web development.Fri, 14 Nov 2025 00:00:00 GMT<p>Every week, the JavaScript ecosystem produces more innovation than one person can reasonably track—new tools, library updates, surprising experiments, and community conversations. Friday Links #32 brings all of it together in one place. This edition highlights standout releases, useful utilities, performance-focused tools, and forward-thinking ideas shaping the next wave of frontend development. Whether you're building production apps or exploring new workflows, this roundup will keep you in the loop.</p>
<p><img src="./images/friday-32.png" alt="Friday Links #32" /></p>
<h2>GPT-5.1 Emerges as a Top Performer in Coding Benchmarks</h2>
<p>GPT-5.1 arrived in the API just a day after its ChatGPT release and is now integrated into the Codex coding agent along with new development tools. Built on the GPT-5 architecture, it inherits strong long-context reasoning, enabling deep repository analysis and high-quality patch generation.</p>
<p>On benchmarks, GPT-5.1 shows consistent gains over GPT-5. In SWE-bench Verified—where models fix real bugs—it achieved 76.3%, up from 72.8%, placing it among the best coding models available. Other tests (MMMU, GPQA, Tau²-bench) also show targeted improvements, putting GPT-5.1 alongside the latest Claude Sonnet and Doubao-Seed-Code models.</p>
<p>OpenAI also upgraded Codex with gpt-5.1-codex and gpt-5.1-codex-mini, tuned for long code analysis and agent workflows. Two major tools were added to the API:</p>
<ul>
<li><code>apply_patch</code> for clean diff-based patch generation</li>
<li><code>shell</code> for safe, sandboxed command-line access</li>
</ul>
<p>These updates are already being integrated into IDEs and agent systems that rely on multi-step edits and test-driven feedback loops.</p>
<p>API pricing for GPT-5.1 Thinking remains the same as GPT-5:</p>
<ul>
<li>$1.25 per 1M input tokens</li>
<li>$10 per 1M output tokens</li>
<li>$0.125 per 1M cached tokens</li>
</ul>
<p>Prompt caching now lasts 24 hours, reducing costs for long sessions and large-project coding workflows.</p>
<h2>📜 Articles & Tutorials</h2>
<p>Google researchers released a 50-page guide on building practical, useful AI agents. It covers agent architecture, how an LLM operates inside an agent, tool integration and configuration, multi-agent coordination, and methods for evaluating performance. Original guide is available <a href="https://www.kaggle.com/whitepaper-introduction-to-agents">here</a>.</p>
<p><a href="https://nilostolte.github.io/tech/articles/ZigCool.html">Why is Zig so Cool?</a></p>
<p><a href="https://medium.com/@sohail_saifi/should-you-stop-using-prisma-421aba045846">Should You Stop Using Prisma? Why Database ORMs Might Be the Worst Thing That Happened to Backend Development</a></p>
<p><a href="https://github.blog/developer-skills/programming-languages-and-frameworks/typescripts-rise-in-the-ai-era-insights-from-lead-architect-anders-hejlsberg/">TypeScript’s rise in the AI era: Insights from Lead Architect, Anders Hejlsberg</a></p>
<p><a href="https://piccalil.li/blog/programming-principles-for-self-taught-front-end-developers/">Programming principles for self taught front-end developers</a></p>
<p><a href="https://frontendmasters.com/blog/perfectly-pointed-tooltips-to-the-corners/">Perfectly Pointed Tooltips: To The Corners</a></p>
<p><a href="https://ergaster.org/posts/2025/10/29-vscode-to-helix/">From VS Code to Helix</a></p>
<p><a href="https://spin.atomicobject.com/animations-threejs-gsap/">Create Sick Web Animations in Three.js with GSAP</a></p>
<p><a href="https://github.com/anthropics/claude-cookbooks/blob/293cde3d3fe1e29ce90b535ccfd311c289302d0c/coding/prompting_for_frontend_aesthetics.ipynb">Frontend Aesthetics: A Prompting Guide</a></p>
<p><a href="https://engineering.fb.com/2025/11/11/web/stylex-a-styling-library-for-css-at-scale/">StyleX: A Styling Library for CSS at Scale</a></p>
<p><a href="https://marmelab.com/blog/2025/10/29/from-graphql-to-zod.html">From GraphQL to Zod: Simplifying Arte's API Architecture</a></p>
<p><a href="https://developers.redhat.com/articles/2025/11/04/3-mcp-servers-you-should-be-using">3 MCP servers you should be using (safely)</a></p>
<p><a href="https://blog.yakkomajuri.com/blog/python-to-node">Why we migrated from Python to Node.js</a></p>
<p><a href="https://physical-ai.ghost.io/a-one-file-pwa-to-tell-you-when-time-is-lying/">A One File PWA to Tell You When Time Is Lying</a></p>
<p><a href="https://minimaxir.com/2025/11/nano-banana-prompts/">Nano Banana can be prompt engineered for extremely nuanced AI image generation</a></p>
<p><a href="https://blog.tensorflow.org/2020/05/pose-animator-open-source-tool-to-bring-svg-characters-to-life.html">Pose Animator - An open source tool to bring SVG characters to life in the browser via motion capture</a></p>
<h2>⚒️ Tools</h2>
<p><a href="https://filemock.com/">FileMock: A Free Tool for Generating Test Files in Any Format</a></p>
<p><a href="https://github.com/cloudflare/vibesdk">vibesdk</a> - An open-source vibe coding platform that helps you build your own vibe-coding platform, built entirely on Cloudflare stack</p>
<p><a href="https://github.com/Vanilagy/mediabunny">Mediabunny</a>: Read and Convert Media Files Directly in the Browser</p>
<p><a href="https://github.com/attogram/bash-screensavers">bash-screensavers</a> – Turn your idle shell into a visual playground.</p>
<p><a href="https://github.com/google/osv-scanner">Google osv-scanner</a></p>
<p><a href="https://placeholderimage.io/">Placeholder Image Generator</a></p>
<h3>Tiny Docker Healthcheck Tools That Cut Image Size by Megabytes</h3>
<p>A developer released <a href="https://github.com/tarampampam/microcheck">microcheck</a>, a set of ultra-small, statically compiled utilities designed specifically for Docker healthchecks. Instead of installing curl or wget—adding 10MB or more to an image—you can drop in a single 70–500 KB binary that works even in scratch and distroless environments.</p>
<p>The toolkit includes:</p>
<ul>
<li><code>httpcheck</code> — HTTP endpoint checks (~75 KB)</li>
<li><code>httpscheck</code> — same, with TLS and protocol autodetection (~500 KB)</li>
<li><code>portcheck</code> — TCP/UDP port checks (~70 KB)</li>
</ul>
<p>Each tool returns standard Docker exit codes (0 = healthy, 1 = unhealthy), making them ideal for bare Docker setups or runtimes without built-in healthchecks.</p>
<p>Example:</p>
<pre><code># Before (+10MB)
RUN apt update && apt install -y curl
HEALTHCHECK CMD curl -f http://localhost:8080/ || exit 1
# After (+75KB)
COPY --from=ghcr.io/tarampampam/microcheck /bin/httpcheck /bin/httpcheck
HEALTHCHECK CMD ["httpcheck", "http://localhost:8080/"]
</code></pre>
<p>They support major architectures (x86_64, ARM, ppc64le, s390x) and provide ready-to-use images.</p>
<p>Source code, examples, and binaries:
<a href="https://github.com/tarampampam/microcheck">microcheck</a></p>
<p><a href="https://github.com/raghavyuva/nixopus">Nixopus</a> - Open Source alternative to vercel, heroku, netlify with Terminal integration, and Self Hosting capabilities.</p>
<p><a href="https://github.com/podman-desktop/podman-desktop">podman-desktop</a> - Podman Desktop is the best free and open source tool to work with Containers and Kubernetes for developers. Get an intuitive and user-friendly interface to effortlessly build, manage, and deploy containers and Kubernetes — all from your desktop.</p>
<p><a href="https://react-syntax-highlighter.github.io/react-syntax-highlighter/demo/">React Syntax Highlighter Demo</a></p>
<p><a href="https://github.com/lone-cloud/gerbil">Gerbil</a> - A desktop app to easily run Large Language Models locally.</p>
<p><a href="https://github.com/OneUptime/oneuptime">OneUptime</a>: The Complete Open-Source Observability Platform</p>
<h2>📚 Libs</h2>
<p><a href="https://github.com/wavetermdev/waveterm">waveterm</a> - An open-source, cross-platform terminal for seamless workflows</p>
<p><a href="https://github.com/sindresorhus/image-dimensions…Howto Build a Theme Switcher with DaisyUI and TanStack Routerhttps://jsdev.space/howto/daisyui-theme-switcher/https://jsdev.space/howto/daisyui-theme-switcher/Learn how to create a TypeScript theme switcher using DaisyUI and TanStack Router in a modern Vite project with the new Tailwind plugin syntax.Tue, 11 Nov 2025 00:00:00 GMT<p>Modern web applications often require dark mode support by default. Using <strong>DaisyUI</strong> with <strong>TanStack Router</strong>, developers can implement a clean, responsive theme switcher that stores user preferences and applies them across routes without additional configuration files.</p>
<p>This guide demonstrates how to build such a switcher in a <strong>Vite + TypeScript</strong> project using the new Tailwind CSS plugin syntax.</p>
<hr />
<h2>1. Project Setup</h2>
<p>A new project is created with TanStack Router and React:</p>
<pre><code>npm create vite@latest
# Select: TanStack Router + React + TypeScript
cd your-app
npm install
</code></pre>
<p>Install DaisyUI:</p>
<pre><code>npm install -D daisyui
</code></pre>
<hr />
<h2>2. Tailwind and DaisyUI Configuration</h2>
<p>The latest Tailwind version supports plugin syntax directly in CSS. A configuration file is no longer needed.</p>
<p>Create or edit <code>src/styles.css</code>:</p>
<pre><code>@import 'tailwindcss';
@plugin 'daisyui' {
themes:
light --default,
dark --prefersdark;
}
</code></pre>
<p>This configuration enables two DaisyUI themes — light and dark — with system preference detection.</p>
<hr />
<h2>3. Creating the Theme Provider</h2>
<p><code>src/providers/theme-provider.tsx</code> manages the theme state and synchronizes it with both the document and local storage.</p>
<pre><code>import { createContext, useContext, useEffect, useMemo, useState } from 'react';
export type Theme = 'light' | 'dark';
const THEME_KEY = 'vite-ui-theme';
type ThemeProviderState = {
theme: Theme;
setTheme: (next: Theme) => void;
};
const ThemeProviderContext = createContext<ThemeProviderState | undefined>(undefined);
function canUseDOM() {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
export function ThemeProvider({
children,
defaultTheme = 'system',
storageKey = THEME_KEY,
...props
}: {
children: React.ReactNode;
defaultTheme?: Theme | 'system';
storageKey?: string;
}) {
const [theme, setThemeState] = useState<Theme>(() => {
if (!canUseDOM()) {
return defaultTheme === 'system' ? 'light' : (defaultTheme as Theme);
}
try {
const saved = localStorage.getItem(storageKey) as Theme | null;
if (saved === 'light' || saved === 'dark') return saved;
if (defaultTheme === 'system') {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return defaultTheme as Theme;
} catch {
return defaultTheme === 'system' ? 'light' : (defaultTheme as Theme);
}
});
useEffect(() => {
if (!canUseDOM()) return;
document.documentElement.setAttribute('data-theme', theme);
try {
localStorage.setItem(storageKey, theme);
} catch {}
}, [theme, storageKey]);
useEffect(() => {
if (!canUseDOM()) return;
const onStorage = (e: StorageEvent) => {
if (e.key === storageKey && (e.newValue === 'light' || e.newValue === 'dark')) {
setThemeState(e.newValue);
}
};
window.addEventListener('storage', onStorage);
return () => window.removeEventListener('storage', onStorage);
}, [storageKey]);
const setTheme = (next: Theme) => setThemeState(next);
const value = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<ThemeProviderContext.Provider value={value} {...props}>
{children}
</ThemeProviderContext.Provider>
);
}
export function useTheme() {
const ctx = useContext(ThemeProviderContext);
if (!ctx) throw new Error('useTheme must be used within a ThemeProvider');
return ctx;
}
</code></pre>
<p>This provider:</p>
<ul>
<li>Initializes the theme from local storage or system preference.</li>
<li>Updates the <code>data-theme</code> attribute on <code><html></code> for DaisyUI.</li>
<li>Synchronizes theme changes across browser tabs.</li>
</ul>
<hr />
<h2>4. Adding the Theme Toggler</h2>
<p><code>src/components/ui/theme-toggler.tsx</code> defines an accessible toggle that switches between light and dark themes.</p>
<pre><code>import { useTheme } from '@/providers/theme-provider';
export default function ThemeToggler() {
const { theme, setTheme } = useTheme();
const isDark = theme === 'dark';
return (
<button
type="button"
className="btn btn-ghost btn-sm"
aria-pressed={isDark}
aria-label="Toggle color theme"
title="Toggle color theme"
onClick={() => setTheme(isDark ? 'light' : 'dark')}
>
<span className="inline-flex items-center gap-2">
<svg aria-hidden viewBox="0 0 24 24" className="size-5">
<g stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
{isDark ? (
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
) : (
<>
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" />
</>
)}
</g>
</svg>
<span className="hidden md:inline">{isDark ? 'Dark' : 'Light'}</span>
</span>
</button>
);
}
</code></pre>
<p>The button toggles the theme and saves the choice in local storage.</p>
<hr />
<h2>5. Integration with TanStack Router</h2>
<p>The theme provider should wrap the entire shell component in <code>routes/__root.tsx</code>:</p>
<pre><code>import { HeadContent, Scripts, createRootRouteWithContext } from '@tanstack/react-router';
import { TanStackDevtools } from '@tanstack/react-devtools';
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
import { ThemeProvider } from '@/providers/theme-provider';
import Header from '../components/header/header';
import appCss from '../styles.css?url';
import type { QueryClient } from '@tanstack/react-query';
interface MyRouterContext {
queryClient: QueryClient;
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ title: 'DaisyUI Theme Switcher' },
],
links: [{ rel: 'stylesheet', href: appCss }],
}),
shellComponent: RootDocument,
});
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<ThemeProvider>
<Header />
{children}
<TanStackDevtools
config={{ position: 'bottom-right' }}
plugins={[
{ name: 'Tanstack Router', render: <TanStackRouterDevtoolsPanel /> },
]}
/>
<Scripts />
</ThemeProvider>
</body>
</html>
);
}
</code></pre>
<hr />
<h2>6. Example Header Integration</h2>
<p><code>src/components/header/header.tsx</code> includes the toggle button alongside navigation links.</p>
<pre><code>import { Link } from '@tanstack/react-router';
import ThemeToggler from '../ui/theme-toggler';
export default function Header() {
return (
<header className="px-2 xl:px-40 my-2.5 flex items-center justify-between gap-2 md:gap-4 xl:gap-6 lg:gap-12">
<div className="flex items-center justify-between w-full">
<button
className="btn btn-ghost text-xl"
>
Logo
</button>
<nav className="flex items-center gap-4" aria-label="Main">
<Link to="/" className="link link-hover">Home</Link>
<ThemeToggler />
</nav>
</div>
</header>
);
}
</code></pre>
<p><strong>Result</str…Building an Accessible Before/After Slider in Reacthttps://jsdev.space/react-before-after-slider/https://jsdev.space/react-before-after-slider/Upgrade a before/after image comparison slider in React with Pointer Events, full keyboard support, A11Y (role="slider"), and resize-safe math.Fri, 07 Nov 2025 00:00:00 GMT<p>If you’ve ever wanted to show a visual transformation — like a <strong>photo edit</strong>, a <strong>UI redesign</strong>, or a <strong>before-and-after effect</strong> — you’ve probably seen those interactive sliders where you can drag a handle to reveal changes.</p>
<p>In this tutorial, we’ll build one of those sliders entirely in <strong>React</strong>, from scratch — no dependencies, no CSS hacks, and full <strong>keyboard and accessibility support</strong>.<br />
By the end, you’ll have a reusable <code><Slider /></code> component you can drop into any project.</p>
<hr />
<h2>Why This Matters</h2>
<p>Before/after sliders are perfect for:</p>
<ul>
<li>Product comparisons (old vs new)</li>
<li>Image editing showcases</li>
<li>Design before/after reveals</li>
<li>Visual storytelling</li>
</ul>
<p>The problem?<br />
Most examples online use outdated event handlers, don’t handle touch events, or completely ignore accessibility.</p>
<p>Let’s fix that. We’ll use:</p>
<ul>
<li><strong>Pointer Events</strong> — for unified mouse & touch input</li>
<li><strong>ARIA roles</strong> — so it’s usable with a keyboard or screen reader</li>
<li><strong>React hooks</strong> — for clear, modern logic</li>
<li><strong>TailwindCSS</strong> — for concise, scalable styling</li>
</ul>
<hr />
<h2>Step 1 — Setup the Component</h2>
<p>Create a new file called <code>Slider.tsx</code> (or <code>.jsx</code> if you prefer).<br />
We’ll start with a minimal structure and a 50/50 default split between “before” and “after” images.</p>
<pre><code>'use client';
import React from 'react';
const Slider: React.FC = () => {
const [slider, setSlider] = React.useState(50);
const [dragging, setDragging] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement | null>(null);
const pointerIdRef = React.useRef<number | null>(null);
const beforeImage = 'https://iili.io/KtZ58mJ.md.png';
const afterImage = 'https://iili.io/KtZ5gXR.png';
</code></pre>
<p>This gives us state for:</p>
<ul>
<li><code>slider</code>: the current divider position (in percent)</li>
<li><code>dragging</code>: whether we’re currently dragging the handle</li>
<li><code>containerRef</code>: the image container reference</li>
<li><code>pointerIdRef</code>: the pointer that owns the drag session</li>
</ul>
<hr />
<h2>Step 2 — Handling Drag and Resize</h2>
<p>Now let’s convert the pointer’s X position to a percentage inside the container.<br />
This makes the slider responsive to any container width.</p>
<pre><code>const clamp = (n: number) => Math.max(0, Math.min(100, n));
const clientToPercent = React.useCallback((clientX: number) => {
const el = containerRef.current;
if (!el) return slider;
const rect = el.getBoundingClientRect();
const x = Math.min(Math.max(clientX - rect.left, 0), rect.width);
return clamp((x / rect.width) * 100);
}, [slider]);
</code></pre>
<p>Then we’ll use <strong>Pointer Events</strong> to track dragging:</p>
<pre><code>const onPointerDown = (e: React.PointerEvent) => {
pointerIdRef.current = e.pointerId;
(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId);
setDragging(true);
setSlider(clientToPercent(e.clientX));
};
React.useEffect(() => {
if (!dragging) return;
const onMove = (e: PointerEvent) => setSlider(clientToPercent(e.clientX));
const onUp = () => setDragging(false);
window.addEventListener('pointermove', onMove, { passive: true });
window.addEventListener('pointerup', onUp, { passive: true });
return () => {
window.removeEventListener('pointermove', onMove);
window.removeEventListener('pointerup', onUp);
};
}, [dragging, clientToPercent]);
</code></pre>
<p>This way, dragging works <strong>even if your cursor leaves the component</strong> — and supports both mouse and touch automatically.</p>
<hr />
<h2>Step 3 — Keyboard Accessibility</h2>
<p>A11Y matters.<br />
Let’s add full keyboard support — with arrows, PageUp/PageDown, Home, and End.</p>
<pre><code>const onKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {
const step = e.shiftKey ? 5 : 2;
const big = 10;
switch (e.key) {
case 'ArrowLeft': e.preventDefault(); setSlider(s => clamp(s - step)); break;
case 'ArrowRight': e.preventDefault(); setSlider(s => clamp(s + step)); break;
case 'PageDown': e.preventDefault(); setSlider(s => clamp(s - big)); break;
case 'PageUp': e.preventDefault(); setSlider(s => clamp(s + big)); break;
case 'Home': e.preventDefault(); setSlider(0); break;
case 'End': e.preventDefault(); setSlider(100); break;
}
};
</code></pre>
<hr />
<h2>Step 4 — Rendering the Markup</h2>
<p>We’ll overlay the “after” image above the “before” one and reveal it using a CSS <code>clip-path</code>.</p>
<pre><code>return (
<div className="relative w-full max-w-lg mx-auto">
<div
ref={containerRef}
className="relative w-full rounded-2xl overflow-hidden select-none"
style={{ aspectRatio: '4 / 3', touchAction: 'none' }}
role="group"
aria-label="Before and after image comparison"
>
<img src={beforeImage} alt="Before" className="absolute inset-0 w-full h-full object-cover" />
<div className="absolute inset-0 overflow-hidden" style={{ clipPath: `inset(0 ${100 - slider}% 0 0)` }}>
<img src={afterImage} alt="After" className="w-full h-full object-cover" />
</div>
<div className="absolute top-0 bottom-0 w-px bg-white/90" style={{ left: `${slider}%` }} />
</code></pre>
<p>Then we’ll add the draggable handle with ARIA attributes and a smooth hover effect:</p>
<pre><code> <button
type="button"
className="absolute top-1/2 -translate-y-1/2 group"
style={{ left: `${slider}%`, transform: 'translate(-50%, -50%)' }}
onPointerDown={onPointerDown}
onKeyDown={onKeyDown}
role="slider"
aria-label="Move comparison slider"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={Math.round(slider)}
>
<span className="grid place-items-center w-8 h-8 rounded-full bg-white shadow-lg ring-1 ring-black/10">
<span className="grid place-items-center w-6 h-6 rounded-full bg-black/5">
<span className="w-1 h-4 rounded-full bg-black/60" />
</span>
</span>
</button>
</div>
</div>
);
</code></pre>
<hr />
<h2>Step 5 — Add Optional Labels and Styles</h2>
<p>Want “BEFORE” and “AFTER” text overlays? Just drop these inside the container:</p>
<pre><code><div className="absolute bottom-4 left-4 text-xs font-medium text-white/80">AFTER</div>
<div className="absolute bottom-4 right-4 text-xs font-medium text-white">BEFORE</div>
</code></pre>
<hr />
<h2>Step 6 — Test and Reuse</h2>
<p>Now test it on desktop, mobile, and with a keyboard — everything should work smoothly.</p>
<p>You can tweak:</p>
<ul>
<li>The <strong>default percentage</strong></li>
<li>The <strong>transition</strong> (add CSS <code>transition</code> for smoother motion)</li>
<li>The <strong>aspect ratio</strong> (change <code>aspectRatio</code> or Tailwind class)</li>
</ul>
<hr />
<p>Full Component Code</p>
<pre><code>'use client';
import React from 'react';
const Slider: React.FC = () => {
const [slider, setSlider] = React.useState(50);
const [dragging, setDragging] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement | null>(null);
const pointerIdRef = React.useRef<number | null>(null);
const beforeImage = 'https://iili.io/KtZ58mJ.md.png';
const afterImage = 'https://iili.io/KtZ5gXR.png';
const clamp = (n: number) => Math.max(0, Math.min(100, n));
const clientToPercent = React.useCallback((clientX: number) => {
const el = containerRef.current;
if (!el) return slider;
const rect = el.getBoundingClientRect();
…10 Criminally Underrated JS Features That Slash Boilerplatehttps://jsdev.space/underrated-js-features/https://jsdev.space/underrated-js-features/Discover 10 overlooked JavaScript features—Sets, Intl, URL APIs, pipeline-ready patterns, allSettled, top-level await, and more—to cut boilerplate fast.Fri, 07 Nov 2025 00:00:00 GMT<p>While browsing a developer thread recently, I came across a discussion about the most <strong>underrated JavaScript features</strong> — features that quietly save hundreds of lines of code and make your apps leaner. Here's a curated list with fresh examples and rewritten code for 2025.</p>
<hr />
<h2>1. Set — Instant Deduplication + O(1) Lookups</h2>
<p><strong>Use cases:</strong> Remove duplicates, prevent event re-binding.</p>
<pre><code>const idPool = [101, 102, 102, 103, 103, 103];
const dedupedIds = [...new Set(idPool)];
console.log(dedupedIds); // [101, 102, 103]
</code></pre>
<p>Prevent duplicate event bindings:</p>
<pre><code>const wiredEvents = new Set();
function bindOnce(evtName, handler) {
if (wiredEvents.has(evtName)) return;
window.addEventListener(evtName, handler);
wiredEvents.add(evtName);
}
bindOnce('scroll', () => console.log('scrolling'));
bindOnce('scroll', () => console.log('scrolling'));
</code></pre>
<hr />
<h2>2. Object.entries() + Object.fromEntries() — Object Transformer Power Duo</h2>
<p>Filter null or empty values from objects:</p>
<pre><code>const rawProfile = {
fullName: 'Zhang San',
age: 28,
avatar: '',
phone: '13800138000',
};
const compactProfile = Object.fromEntries(
Object.entries(rawProfile).filter(([k, v]) => v !== '')
);
console.log(compactProfile);
// { fullName: 'Zhang San', age: 28, phone: '13800138000' }
</code></pre>
<p>Parse URL queries without regex:</p>
<pre><code>const queryString = window.location.search.slice(1);
const queryMap = Object.fromEntries(new URLSearchParams(queryString));
console.log(queryMap);
</code></pre>
<hr />
<h2>3. ?? and ??= — Safer Defaults than ||</h2>
<p>Unlike <code>||</code>, nullish coalescing doesn’t override valid falsy values like <code>0</code> or <code>''</code>.</p>
<pre><code>const qtyInput = 0;
const wrongQty = qtyInput || 10;
const safeQty = qtyInput ?? 10;
console.log(wrongQty, safeQty); // 10, 0
</code></pre>
<p>Add default fields safely:</p>
<pre><code>const reqOpts = { timeout: 5000 };
reqOpts.retries ??= 3;
console.log(reqOpts);
</code></pre>
<hr />
<h2>4. Intl — Native i18n for Currency, Numbers, Dates</h2>
<p>Format prices easily:</p>
<pre><code>const priceValue = 1234.56;
const priceCNY = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(priceValue);
const priceUSD = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(priceValue);
console.log(priceCNY, priceUSD);
</code></pre>
<p>Localized dates:</p>
<pre><code>const clock = new Date();
const zhDate = new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }).format(clock);
const enDate = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format(clock);
console.log(zhDate, enDate);
</code></pre>
<hr />
<h2>5. IntersectionObserver — Lazy Images + Infinite Lists</h2>
<p>Lazy-load images:</p>
<pre><code><img data-src="https://site.com/photo.jpg" src="placeholder.jpg" class="js-lazy" />
</code></pre>
<pre><code>const imageWatcher = new IntersectionObserver((batch) => {
for (const entry of batch) {
if (!entry.isIntersecting) continue;
const pic = entry.target;
pic.src = pic.dataset.src;
imageWatcher.unobserve(pic);
}
});
document.querySelectorAll('.js-lazy').forEach((img) => imageWatcher.observe(img));
</code></pre>
<p>Infinite scrolling:</p>
<pre><code>const sentinel = document.getElementById('load-flag');
const listWatcher = new IntersectionObserver(async ([entry]) => {
if (!entry.isIntersecting) return;
const newItems = await fetchNextPage();
renderList(newItems);
});
listWatcher.observe(sentinel);
</code></pre>
<hr />
<h2>6. Promise.allSettled() — Batch Requests Without Failing Fast</h2>
<p>Unlike <code>Promise.all</code>, <code>allSettled()</code> waits for every result.</p>
<pre><code>const batchTasks = [
fetch('/api/user/101'),
fetch('/api/orders/101'),
fetch('/api/messages/101'),
];
const outcomes = await Promise.allSettled(batchTasks);
const okPayloads = await Promise.all(outcomes.filter(r => r.status === 'fulfilled').map(r => r.value.json()));
const badEndpoints = outcomes.filter(r => r.status === 'rejected').map(r => r.reason?.url ?? '(unknown)');
console.log('OK:', okPayloads);
console.log('Failed:', badEndpoints);
</code></pre>
<hr />
<h2>7. Element.closest() — Reliable DOM Traversal</h2>
<p>Highlight container when clicking a child:</p>
<pre><code><ul class="people-list">
<li class="people-item">Alice</li>
<li class="people-item">Bob</li>
</ul>
</code></pre>
<pre><code>document.querySelectorAll('.people-item').forEach((node) => {
node.addEventListener('click', (ev) => {
const wrapper = ev.target.closest('.people-list');
wrapper?.classList.toggle('is-active');
});
});
</code></pre>
<hr />
<h2>8. URL + URLSearchParams — No More Regex URL Manipulation</h2>
<pre><code>const current = new URL(window.location.href);
console.log(current.hostname);
console.log(current.pathname);
console.log(current.searchParams.get('name'));
</code></pre>
<p>Modify query parameters:</p>
<pre><code>const link = new URL('https://example.com/page');
link.searchParams.append('page', '3');
link.searchParams.append('limit', '10');
link.searchParams.set('page', '4');
link.searchParams.delete('limit');
console.log(link.href);
</code></pre>
<hr />
<h2>9. for...of — Iterable-Friendly and Breakable</h2>
<p>Stop mid-loop when found:</p>
<pre><code>const catalog = [
{ id: 1, label: 'Phone', price: 5999 },
{ id: 2, label: 'Laptop', price: 9999 },
{ id: 3, label: 'Tablet', price: 3999 },
];
for (const item of catalog) {
if (item.price > 8000) {
console.log('High-value item:', item);
break;
}
}
</code></pre>
<p>Iterate a Set with indexes:</p>
<pre><code>const tagBag = new Set(['Frontend', 'JS', 'CSS']);
for (const [idx, token] of [...tagBag].entries()) {
console.log(`Index ${idx}: ${token}`);
}
</code></pre>
<hr />
<h2>10. Top-Level Await — Async Modules Without Wrappers</h2>
<p><strong>config.mjs</strong></p>
<pre><code>const resp = await fetch('/api/config');
export const runtimeConfig = await resp.json();
</code></pre>
<p><strong>api.mjs</strong></p>
<pre><code>import { runtimeConfig } from './config.mjs';
export const httpClient = {
base: runtimeConfig.baseUrl,
get(path) {
return fetch(`${this.base}${path}`);
},
};
</code></pre>
<p>Dynamic import on user action:</p>
<pre><code>document.getElementById('btn-charts').addEventListener('click', async () => {
const { mountChart } = await import('./charts.mjs');
mountChart('#chart-area');
});
</code></pre>
<hr />
<p>Wrap-Up</p>
<p>A lot of what we used to need libraries for—<code>uniq</code>, <code>moment</code>, lazy loading—now has native APIs.<br />
Modern JavaScript has matured: it’s fast, expressive, and powerful right out of the box.<br />
Embrace these hidden gems, and you’ll spend more time solving real problems, not rewriting helpers.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/algorithm-complexity-big-o/">Understanding Big O Notation with JavaScript</a></li>
<li><a href="/animejs-animation-guide/">Mastering Web Animations with Anime.js</a></li>
<li><a href="/await-fetch-slow/">Optimizing await fetch(): Why It Slows Down and How to Speed It Up</a></li>
</ul>
Howto Use EXISTS and NOT EXISTS in SQLhttps://jsdev.space/howto/sql-exists-vs-not-exists/https://jsdev.space/howto/sql-exists-vs-not-exists/Learn the key differences between EXISTS and NOT EXISTS in SQL, how they work internally, and when to use each for efficient subquery filtering.Thu, 06 Nov 2025 00:00:00 GMT<p>In SQL, filtering results based on the presence or absence of related data often involves the keywords <strong>EXISTS</strong> and <strong>NOT EXISTS</strong>. While they may look similar, their behavior and performance characteristics can vary depending on the database engine and query structure.</p>
<p>This article breaks down how each works, when to use them, and how to avoid common mistakes.</p>
<p>What Is EXISTS?</p>
<p>The <code>EXISTS</code> operator checks whether a <strong>subquery returns at least one row</strong>.
If the subquery finds any result, <code>EXISTS</code> returns <code>TRUE</code>; otherwise, it returns <code>FALSE</code>.</p>
<h3>Example</h3>
<pre><code>SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
</code></pre>
<p><strong>Explanation:</strong> This query returns all customers <strong>who have at least one order</strong>.
The subquery runs per row of the main query, stopping immediately when a match is found — making it efficient for large datasets with indexes.</p>
<p>What Is NOT EXISTS?</p>
<p><code>NOT EXISTS</code> is the logical opposite — it checks that a subquery <strong>returns no rows</strong>.
If the subquery finds even one row, the condition becomes <code>FALSE</code>.</p>
<h3>Example</h3>
<pre><code>SELECT c.customer_id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
</code></pre>
<p><strong>Explanation:</strong> This query returns customers <strong>who have never placed an order</strong>.
It’s useful for identifying missing relationships, such as users without activity or products not yet sold.</p>
<p>Internal Behavior and Performance</p>
<ul>
<li><strong>EXISTS</strong>: stops as soon as it finds the first matching row (short-circuit evaluation).</li>
<li><strong>NOT EXISTS</strong>: must confirm that <strong>no</strong> matching rows exist, which can be slower on large tables.</li>
</ul>
<p>Many modern databases (PostgreSQL, MySQL 8+, SQL Server) optimize these checks effectively, especially when proper indexes are in place.</p>
<p>Practical Example</p>
<p>Suppose you have two tables:</p>
<pre><code>-- schemas
users(id, name)
purchases(id, user_id, amount)
</code></pre>
<h3>Find users with purchases</h3>
<pre><code>SELECT name
FROM users u
WHERE EXISTS (
SELECT 1
FROM purchases p
WHERE p.user_id = u.id
);
</code></pre>
<h3>Find users without purchases</h3>
<pre><code>SELECT name
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM purchases p
WHERE p.user_id = u.id
);
</code></pre>
<p>The difference lies in <strong>what you're testing for</strong> — the existence or nonexistence of related data.</p>
<p>EXISTS vs IN vs JOIN</p>
<table>
<thead>
<tr>
<th>Operator</th>
<th>Use Case</th>
<th>Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>EXISTS</code></td>
<td>Check if subquery has any rows</td>
<td>Stops at the first match</td>
</tr>
<tr>
<td><code>NOT EXISTS</code></td>
<td>Check for missing relationships</td>
<td>Confirms no matches exist</td>
</tr>
<tr>
<td><code>IN</code></td>
<td>Compare values directly</td>
<td>Can be slower with large subquery sets</td>
</tr>
<tr>
<td><code>LEFT JOIN ... IS NULL</code></td>
<td>Alternative to <code>NOT EXISTS</code></td>
<td>Watch for duplicates if joins fan out</td>
</tr>
</tbody>
</table>
<p>Best Practices</p>
<ol>
<li><strong>Prefer <code>EXISTS</code> for correlated subqueries</strong> — it’s often clearer and faster than <code>IN</code>.</li>
<li><strong>Index</strong> the columns used in the subquery’s join condition.</li>
<li><strong>Beware NULLs</strong> with <code>NOT EXISTS</code>: ensure subquery filters exclude unwanted NULL semantics.</li>
<li><strong>Measure</strong> on your database — optimizers differ by engine and version.</li>
</ol>
<p>Conclusion</p>
<p><code>EXISTS</code> and <code>NOT EXISTS</code> are powerful tools for expressing relationships between datasets.</p>
<ul>
<li>Use <strong>EXISTS</strong> to find records <strong>with</strong> matching entries.</li>
<li>Use <strong>NOT EXISTS</strong> to find records <strong>without</strong> matches.</li>
</ul>
<p>With sound indexing and careful structure, they make queries cleaner, faster, and more expressive.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/calendar-table-sql/">Build Dynamic Date Ranges in SQL with Ease</a></li>
<li><a href="/howto/add-months-to-date-js/">Practical Month Arithmetic and Calendar Logic in JavaScript</a></li>
<li><a href="/howto/array-flatten/">Mastering Array Flattening in JavaScript</a></li>
</ul>
Howto Configure Webpack: From Basics to Expert Levelhttps://jsdev.space/howto/webpack-configuration-guide/https://jsdev.space/howto/webpack-configuration-guide/Learn how to configure Webpack step-by-step, from simple entry/output setups to advanced plugins, loaders, optimizations, and environment-specific builds.Sun, 02 Nov 2025 00:00:00 GMT<p>Hi developers! This guide will demystify <strong>Webpack configuration</strong>
from beginner to expert. If the <code>webpack.config.js</code> file once looked
like a foreign language, by the end of this article you'll understand
every piece of it --- and how to customize it for real-world projects.</p>
<ol>
<li>Your First Webpack Config (Hello World)</li>
</ol>
<p>Let's start with the simplest example to understand the structure.</p>
<pre><code>// my first webpack configuration
const path = require("path");
module.exports = {
// Entry: where bundling begins
entry: "./src/index.js",
// Output: where bundled files go
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
// Mode: development or production
mode: "development",
};
</code></pre>
<p>Even this tiny config can bundle your app. Think of it as the "Hello
World" of Webpack.</p>
<ol>
<li>Core Concepts: Entry, Output, Loaders, and Plugins</li>
</ol>
<h3>Entry --- The Starting Point</h3>
<pre><code>// Single entry (SPA)
entry: "./src/index.js";
// Multiple entries (multi-page)
entry: {
home: "./src/home.js",
about: "./src/about.js",
contact: "./src/contact.js",
};
</code></pre>
<h3>Output --- The Final Destination</h3>
<pre><code>output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash].js",
clean: true,
publicPath: "https://cdn.example.com/",
};
</code></pre>
<h3>Loaders --- The File Translators</h3>
<p>Loaders tell Webpack how to handle different file types.</p>
<pre><code>module: {
rules: [
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] },
{
test: /\.(png|jpg|jpeg|gif|svg)$/i,
type: "asset/resource",
generator: { filename: "images/[name].[hash][ext]" },
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: "asset/resource",
generator: { filename: "fonts/[name].[hash][ext]" },
},
{
test: /\.js$/,
exclude: /node_modules/,
use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"] } },
},
],
}
</code></pre>
<h3>Plugins --- The Feature Boosters</h3>
<pre><code>const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
title: "My App",
minify: true,
}),
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
}),
new CleanWebpackPlugin(),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("production"),
}),
];
</code></pre>
<ol>
<li>Real-World Setup Example</li>
</ol>
<p>Here's a practical config used in real projects --- complete and
environment-aware.</p>
<pre><code>const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (env, argv) => {
const isProd = argv.mode === "production";
return {
entry: {
main: "./src/index.js",
vendor: "./src/vendor.js",
},
output: {
path: path.resolve(__dirname, "dist"),
filename: isProd ? "[name].[contenthash].js" : "[name].js",
publicPath: "/",
},
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, use: "babel-loader" },
{
test: /\.css$/,
use: [
isProd ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader",
],
},
{ test: /\.(png|jpg|gif)$/i, type: "asset/resource" },
],
},
plugins: [
new HtmlWebpackPlugin({ template: "./src/index.html", inject: true }),
...(isProd
? [new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" })]
: []),
],
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: "vendors",
priority: 10,
},
},
},
},
devServer: {
static: "./dist",
hot: true,
open: true,
port: 3000,
},
devtool: isProd ? "source-map" : "eval-cheap-module-source-map",
};
};
</code></pre>
<ol>
<li>Environment Configs: Dev vs. Production</li>
</ol>
<h3>Development</h3>
<pre><code>// webpack.dev.js
module.exports = {
mode: "development",
devtool: "eval-source-map",
devServer: {
static: "./dist",
hot: true,
open: true,
historyApiFallback: true,
},
module: {
rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }],
},
};
</code></pre>
<h3>Production</h3>
<pre><code>// webpack.prod.js
const TerserPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
devtool: "source-map",
optimization: {
minimize: true,
minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
splitChunks: { chunks: "all" },
},
module: {
rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }],
},
plugins: [new MiniCssExtractPlugin()],
};
</code></pre>
<ol>
<li>Advanced Webpack Tricks</li>
</ol>
<h3>Dynamic Configs</h3>
<pre><code>module.exports = (env, argv) => {
const isAnalyze = env && env.analyze;
const config = { /* base config */ };
if (isAnalyze) {
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
config.plugins.push(new BundleAnalyzerPlugin());
}
return config;
};
</code></pre>
<h3>Multi-Target Builds</h3>
<pre><code>module.exports = [
{
name: "client",
target: "web",
entry: "./src/client.js",
output: { filename: "client.bundle.js" },
},
{
name: "server",
target: "node",
entry: "./src/server.js",
output: { filename: "server.bundle.js" },
},
];
</code></pre>
<ol>
<li>Optimization Essentials</li>
</ol>
<pre><code>optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: "vendors",
priority: 20,
},
common: {
name: "common",
minChunks: 2,
priority: 10,
reuseExistingChunk: true,
},
},
},
runtimeChunk: { name: "runtime" },
};
</code></pre>
<p>Summary</p>
<p>Configuring Webpack is like <strong>building with LEGO</strong> --- start small and
keep improving.</p>
<p>Understand the pillars: <strong>Entry</strong>, <strong>Output</strong>, <strong>Loader</strong>,
<strong>Plugin</strong><br />
Separate development and production configs<br />
Use code-splitting and caching wisely<br />
Optimize only where it matters</p>
<p>Keep experimenting --- Webpack mastery comes from <strong>practice</strong>, not
memorization.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/basic-webpack-project/">Basic Webpack 5 Project with CSS: Step-by-Step</a></li>
<li><a href="/howto/build-first-ai-app-openai-api/">Complete Beginner's Guide to Creating AI Applications with OpenAI</a></li>
<li><a href="/howto/bun-install-vs-bun-add/">Bun Install vs Bun Add: Choosing the Right Command</a></li>
</ul>
7 Bun Workflows to Supercharge Your Full-Stack Developmenthttps://jsdev.space/bun-workflows/https://jsdev.space/bun-workflows/Discover seven Bun workflows to speed up your full-stack development — from workspaces and testing to fast builds and environment setup.Fri, 31 Oct 2025 00:00:00 GMT<p>When someone types <code>npm install</code>, laptop fans roar like a jet engine.
Bun changes that story. It combines a <strong>runtime</strong>, <strong>package manager</strong>,
<strong>bundler</strong>, and <strong>test runner</strong> into one lightning-fast binary. But
using Bun effectively takes more than just installation --- it requires
the right workflows.</p>
<p>Here are <strong>seven practical Bun workflows</strong> to help you write, test, and
deploy apps faster and cleaner.</p>
<h2>1. Run One-Off Commands with <code>bunx</code></h2>
<p>Forget globally installed CLI tools. They clutter your environment and
version management. Instead, <code>bunx</code> lets you run any package command
without installing it globally.</p>
<pre><code># Create a new Vite + React project without global installs
bunx create-vite@latest my-react-app --template react-ts
# Run ESLint and auto-fix issues
bunx eslint . --fix
</code></pre>
<p>Everyone --- from your local machine to CI pipelines --- uses the exact
same tool versions. No more "works on my machine" moments.</p>
<h2>2. Use Reliable Lockfiles with <code>bun install</code></h2>
<p><code>bun install</code> isn't just fast; it ensures reproducible builds by
generating compact and consistent dependencies.</p>
<pre><code># Prevent accidental lockfile changes
bun install --frozen-lockfile
# Production install without devDependencies
bun install --production
</code></pre>
<p>Commit the <strong><code>bun.lockb</code></strong> binary lockfile to your repo and enable
<code>--frozen-lockfile</code> in CI to keep your dependency tree consistent across
all environments.</p>
<h2>3. Simplify Monorepos with Bun Workspaces</h2>
<p>Bun makes managing monorepos effortless --- no need for Nx or Lerna.
Just define your workspace structure in the root <code>package.json</code>:</p>
<pre><code>{
"name": "super-app",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"dev": "bun run --filter \"./apps/*\" dev",
"build": "bun run --filter \"./packages/*\" build"
}
}
</code></pre>
<p>Shared libraries under <code>packages/</code> are instantly available to all apps
under <code>apps/</code>, streamlining multi-app development and versioning.</p>
<h2>4. Combine Front-End and API in a Single Bun Server</h2>
<p>Bun's built-in HTTP server is small and fast. Using frameworks like
<strong>Hono</strong> or <strong>Elysia</strong>, you can serve both your API and static front-end
from the same process.</p>
<pre><code>// server.ts
import { Hono } from "hono";
import { serveStatic } from "hono/bun";
const server = new Hono();
server.get("/api/users/:id", (ctx) => {
const { id } = ctx.req.param();
return ctx.json({ id, name: `User ${id}` });
});
server.use("/*", serveStatic({ root: "./public" }));
export default {
port: 8080,
fetch: server.fetch,
};
</code></pre>
<p>Start it with:</p>
<pre><code>bun --watch run server.ts
</code></pre>
<p>Your API and client reload automatically as you code --- the dream setup
for full-stack development.</p>
<h2>5. Build Ultra-Fast Bundles with <code>bun build</code></h2>
<p>Bun's bundler is extremely efficient for both client and server targets.</p>
<pre><code># Bundle client code for browsers
bun build ./src/main.ts --outdir ./dist/client --sourcemap --minify
# Bundle backend code for Bun or Node.js
bun build ./src/server.ts --target=bun --outdir ./dist/server
</code></pre>
<p>Perfect for microservices, cloud functions, or optimizing CI/CD build
steps.</p>
<h2>6. Test Instantly with <code>bun test</code></h2>
<p>Forget Jest configuration chaos. Bun's built-in test runner works out of
the box with TypeScript, parallel execution, and coverage.</p>
<pre><code>// text-utils.test.ts
import { expect, test, describe } from "bun:test";
const toTitleCase = (text: string) =>
text ? text[0].toUpperCase() + text.slice(1) : "";
describe("toTitleCase()", () => {
test("capitalizes the first letter", () => {
expect(toTitleCase("world")).toBe("World");
});
test("returns empty string for empty input", () => {
expect(toTitleCase("")).toBe("");
});
});
</code></pre>
<p>Run tests with:</p>
<pre><code>bun test
</code></pre>
<p>Enjoy blazing speed with built-in watch mode and coverage reports ---
testing feels frictionless again.</p>
<h2>7. Manage Environment Variables Without <code>dotenv</code></h2>
<p>Bun automatically loads <code>.env</code> files from the project root, no
dependencies required.</p>
<pre><code>DATABASE_URL="postgresql://user:pass@localhost:5432/db"
JWT_SECRET="super-secret-key"
</code></pre>
<p>Then simply access them in your code:</p>
<pre><code>// config.ts
function requireEnv(key: string): string {
const val = Bun.env[key];
if (!val) throw new Error(`Missing environment variable: ${key}`);
return val;
}
export const env = {
db: requireEnv("DATABASE_URL"),
secret: requireEnv("JWT_SECRET"),
};
</code></pre>
<p>This lightweight approach eliminates extra dependencies while enforcing
safer configuration management.</p>
<p>Pitfalls to Watch For</p>
<ul>
<li><strong>Node.js module compatibility:</strong> Not all Node APIs or native C++
modules are supported.\</li>
<li><strong>CJS imports:</strong> Some legacy packages require
<code>import pkg from 'old-lib'</code> and then using <code>pkg.default</code>.\</li>
<li><strong>Binary lockfiles:</strong> <code>bun.lockb</code> merges can't be resolved manually;
rerun <code>bun install</code> after conflicts.</li>
</ul>
<p>Takeaway</p>
<p>Bun is about <strong>integration</strong> --- bundler, test runner, package manager,
and runtime unified into one tool.<br />
It replaces five separate utilities, cuts build times, and simplifies
project setup dramatically.</p>
<p>Start with <a href="https://www.servbay.com/"><strong>ServBay</strong></a> to manage your Node and Bun versions easily, then
dive into these workflows to experience true full-stack acceleration.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/complete-monorepo-guide/">Mastering Modern Monorepo Development with pnpm, Workspaces</a></li>
<li><a href="/files-dirs-nodejs/">Get All Files and Folders in Node.js Directories</a></li>
<li><a href="/first-principles-testing/">Master Code Testing Quality with F.I.R.S.T Methodology</a></li>
</ul>
Howto Optimize React with useCallback, useMemo & React.memohttps://jsdev.space/howto/react-performance-optimization-trio/https://jsdev.space/howto/react-performance-optimization-trio/Understand how useCallback, useMemo, and React.memo workFri, 31 Oct 2025 00:00:00 GMT<p>If you've written React apps for a while, you've probably met the three
performance heroes: <strong>useCallback</strong>, <strong>useMemo</strong>, and <strong>React.memo</strong>.<br />
But when should you use them, and how do they differ? This guide breaks
down each concept with clear explanations and practical examples.</p>
<ol>
<li>Why Components Re-Render</li>
</ol>
<p>Every time a parent's state updates, its entire function re-runs,
recreating variables and functions. That's why child components
re-render "innocently."</p>
<pre><code>function Parent() {
const [count, setCount] = useState(0);
const handleClick = () => console.log("clicked");
console.log("Parent rendered");
return (
<>
<p>Count: {count}</p>
<Child onClick={handleClick} />
<button onClick={() => setCount(c => c + 1)}>+1</button>
</>
);
}
function Child({ onClick }) {
console.log("Child rendered");
return <button onClick={onClick}>Click me</button>;
}
</code></pre>
<p>Each render creates a <strong>new function reference</strong>, so React thinks props
changed.</p>
<pre><code>const fn1 = () => {};
const fn2 = () => {};
console.log(fn1 === fn2); // false
</code></pre>
<ol>
<li><code>React.memo</code>: Skipping Useless Renders</li>
</ol>
<p><code>React.memo</code> is a higher-order component that performs a <strong>shallow
comparison</strong> of props. If they haven't changed, React skips
re-rendering.</p>
<pre><code>const Child = React.memo(({ onClick }) => {
console.log(" Child rendered");
return <button onClick={onClick}>Click me</button>;
});
</code></pre>
<p>It compares new vs. old props. If they're equal → skip render;
otherwise → re-render.</p>
<p>Limitation: Shallow comparison fails for new object or function
references.</p>
<pre><code>// These always create new references
<Child onClick={() => {}} />
<Child config={{ theme: "dark" }} />
</code></pre>
<ol>
<li><code>useCallback</code>: Stable Function References</li>
</ol>
<p><code>useCallback</code> memoizes a function and returns the same reference unless
dependencies change.</p>
<pre><code>const handleClick = useCallback(() => {
console.log("clicked");
}, []);
</code></pre>
<p>Use it with <code>React.memo</code> for stable props:</p>
<pre><code>const Child = React.memo(({ onClick }) => {
console.log(" Child rendered");
return <button onClick={onClick}>Click me</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => console.log("clicked"), []);
return (
<>
<p>Count: {count}</p>
<Child onClick={handleClick} />
<button onClick={() => setCount(c => c + 1)}>+1</button>
</>
);
}
</code></pre>
<p>Now the <code>Child</code> component will no longer re-render when the parent
updates unrelated state.</p>
<p>With dependencies:</p>
<pre><code>const [userId, setUserId] = useState(1);
const fetchUser = useCallback(() => {
fetch(`/api/user/${userId}`);
}, [userId]);
</code></pre>
<ol>
<li><code>useMemo</code>: Cache Expensive Computations</li>
</ol>
<p>While <code>useCallback</code> caches a <strong>function reference</strong>, <code>useMemo</code> caches a
<strong>computed value</strong>.</p>
<p>Hook Caches Returns Common Use</p>
<hr />
<p>useCallback Function reference Function Child callbacks
useMemo Computed value Value Derived states / calculations</p>
<p>Example: Filter large product lists efficiently.</p>
<pre><code>function ProductList({ products }) {
const [filter, setFilter] = useState("");
const filtered = useMemo(() => {
console.log(" Filtering...");
return products.filter(p =>
p.name.toLowerCase().includes(filter.toLowerCase())
);
}, [products, filter]);
return (
<>
<input value={filter} onChange={e => setFilter(e.target.value)} />
{filtered.map(p => (
<div key={p.id}>{p.name}</div>
))}
</>
);
}
</code></pre>
<p>Without <code>useMemo</code>: recalculates on every re-render.<br />
With <code>useMemo</code>: recalculates only when dependencies change.</p>
<ol>
<li>Combined Strategy</li>
</ol>
<ul>
<li><strong><code>useCallback</code></strong> → memoize functions passed to children\</li>
<li><strong><code>useMemo</code></strong> → memoize derived or heavy computations\</li>
<li><strong><code>React.memo</code></strong> → skip rendering children with identical props</li>
</ul>
<p>Together, they stabilize props and eliminate unnecessary renders.</p>
<ol>
<li>Best Practices</li>
</ol>
<hr />
<p>Scenario Recommended Solution Why</p>
<hr />
<p>Passing callback to <code>useCallback</code> + <code>React.memo</code> Stable
child reference</p>
<p>Filtering/sorting <code>useMemo</code> Cache
data computation</p>
<p>Passing <code>useMemo</code> Stable props
objects/arrays</p>
<p>Component props <code>React.memo</code> Avoid
rarely change re-render</p>
<h2>Simple components Skip optimization Overhead not
worth it</h2>
<p>Common Pitfalls</p>
<ul>
<li><strong>Overuse:</strong> Avoid wrapping every function --- measure first!\</li>
<li><strong>Missing React.memo:</strong> <code>useCallback</code> alone won't stop re-renders.\</li>
<li><strong>Missing dependencies:</strong> Always declare dependency arrays to avoid
stale closures.</li>
</ul>
<ol>
<li>Full Example</li>
</ol>
<pre><code>import React, { useState, useMemo, useCallback, memo } from "react";
const ResultDisplay = memo(({ result }) => {
console.log(" ResultDisplay rendered");
return <div>Result: {result}</div>;
});
const ActionButton = memo(({ onAction, label }) => {
console.log(" ActionButton rendered");
return <button onClick={onAction}>{label}</button>;
});
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const heavyResult = useMemo(() => {
console.log(" Heavy calculation...");
let total = 0;
for (let i = 0; i < 1_000_000; i++) total += i;
return total + count;
}, [count]);
const handleAdd = useCallback(() => setCount(c => c + 1), []);
const handleReset = useCallback(() => setCount(0), []);
console.log(" App rendered");
return (
<div>
<h1>React Performance Demo</h1>
<p>Count: {count}</p>
<input
value={text}
onChange={e => setText(e.target.value)}
placeholder="Type something..."
/>
<ResultDisplay result={heavyResult} />
<ActionButton onAction={handleAdd} label="+1" />
<ActionButton onAction={handleReset} label="Reset" />
</div>
);
}
</code></pre>
<ol>
<li>Conclusion</li>
</ol>
<ul>
<li><code>useCallback</code> → remember the <strong>function</strong>\</li>
<li><code>useMemo</code> → remember the <strong>value</strong>\</li>
<li><code>React.memo</code> → remember the <strong>component</strong></li>
</ul>
<p>These hooks work best together when applied intentionally, not
everywhere.<br />
Measure, identify bottlenecks, and optimize where it truly matters.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/createelement-react/">Fixing React’s “Cannot Read Property createElement of Undefined”</a></li>
<li><a href="/howto/daisyui-theme-switcher/">Theme Switcher with DaisyUI and TanStack Router</a></li>
<li><a href="/howto/jest-rtl-setup-react-nextjs/">Properly Configuring Jest for Reac…Howto Unlock Web Workers for Front-End Performancehttps://jsdev.space/howto/web-workers-performance-optimization/https://jsdev.space/howto/web-workers-performance-optimization/Learn how to use Web Workers to eliminate front-end performance bottlenecks through parallel processing, offloading tasks, and smart data handling.Fri, 31 Oct 2025 00:00:00 GMT<p>In modern front-end applications, performance optimization is no longer
optional. When scrolling becomes choppy or the UI freezes, we often turn
to complex frameworks or micro-optimizations. But there's an overlooked
API that solves most of these problems elegantly: <strong>Web Workers</strong>.</p>
<p>Why Web Workers Are Underrated</p>
<p>Introduced in 2009, Web Workers still don't get the attention they
deserve. Common myths include:</p>
<ul>
<li><strong>"Too complicated to use"</strong> → It only takes a few lines of code.</li>
<li><strong>"Not widely supported"</strong> → 98% of browsers support it today.</li>
<li><strong>"Hard to separate logic"</strong> → The performance gain far outweighs
the refactor.</li>
</ul>
<p>Let's explore how this API transforms front-end performance.</p>
<ol>
<li>Freeing the Main Thread: Say Goodbye to Lag</li>
</ol>
<p>JavaScript runs on a single thread, so heavy computations block UI
rendering. Web Workers move that work off the main thread.</p>
<pre><code>// Traditional approach — blocks UI
function computeReport(data) {
const result = performHeavyMath(data); // UI freezes!
updateDashboard(result);
}
// With Web Worker — smooth interface
const worker = new Worker("worker-calc.js");
worker.postMessage(hugeDataset);
worker.onmessage = (event) => updateDashboard(event.data);
</code></pre>
<p>Now your UI stays responsive while computations happen in parallel.</p>
<ol>
<li>True Parallelism on Multi-Core Devices</li>
</ol>
<p>JavaScript typically uses only one CPU core. Web Workers finally allow
true multi-core utilization.</p>
<pre><code>// Create a pool of workers
const taskPool = Array(4)
.fill(0)
.map(() => new Worker("task-handler.js"));
function runParallel(tasks) {
const slice = Math.ceil(tasks.length / taskPool.length);
taskPool.forEach((w, i) => {
const batch = tasks.slice(i * slice, (i + 1) * slice);
w.postMessage(batch);
});
}
</code></pre>
<p>Each worker processes data independently, achieving <strong>3--5× speedups</strong>.</p>
<ol>
<li>Smarter Memory Usage</li>
</ol>
<p>Each worker runs in its own thread with a separate memory space,
preventing main-thread memory spikes.</p>
<p>Example: handling big data filtering.</p>
<pre><code>// filter-worker.js
self.onmessage = (e) => {
const { list, keyword, filters } = e.data;
const start = performance.now();
const result = list
.filter((item) =>
item.name.toLowerCase().includes(keyword.toLowerCase())
)
.filter((item) => filters.every((fn) => fn(item)))
.sort((a, b) => a.rank - b.rank);
const duration = performance.now() - start;
self.postMessage({ result, time: `${duration.toFixed(2)}ms` });
};
</code></pre>
<p>Main thread:</p>
<pre><code>const searchWorker = new Worker("filter-worker.js");
input.addEventListener("input", (e) => {
searchWorker.postMessage({
list: massiveDataset,
keyword: e.target.value,
filters: activeRules,
});
});
searchWorker.onmessage = (e) => {
renderResults(e.data.result);
showStats(e.data.time);
};
</code></pre>
<ol>
<li>Image and Video Processing Off the Main Thread</li>
</ol>
<p>Web Workers shine in real-time image manipulation, compression, or
AI-powered filters.</p>
<pre><code>// image-worker.js
self.onmessage = (e) => {
const { imageData, steps } = e.data;
const processed = applyImageFilters(imageData, steps);
const analysis = runImageAI(processed);
const thumbnails = createThumbnails(processed);
self.postMessage({ processed, analysis, thumbnails });
};
// main thread
fileInput.addEventListener("change", async (e) => {
const file = e.target.files[0];
const data = await getImagePixels(file);
imgWorker.postMessage({
imageData: data,
steps: ["enhance", "denoise", "autoContrast"],
});
});
</code></pre>
<p>Users experience zero lag, even during computation-heavy tasks.</p>
<ol>
<li>Real-Time Visualization with D3.js in Workers</li>
</ol>
<p>Rendering large datasets with D3 or Chart.js often blocks the UI --- but
Web Workers fix that.</p>
<pre><code>// chart-worker.js
self.importScripts("d3.min.js");
self.onmessage = (e) => {
const { data, chartType, size } = e.data;
const layout = calculateLayout(data, size);
const paths = generateSVGPaths(layout, chartType);
const stats = computeStats(data);
self.postMessage({ layout, paths, stats });
};
</code></pre>
<p>Performance Results</p>
<p>Scenario Traditional Web Worker Gain</p>
<hr />
<p>100k Data Search 1200ms (UI freeze) 45ms (smooth) ×26
4K Image Processing 2800ms 650ms ×4.3
Complex Chart Render 850ms 180ms ×4.7</p>
<p>Advanced Techniques</p>
<h3>1. Worker Pool Manager</h3>
<p>Avoid repeatedly creating and destroying workers:</p>
<pre><code>class WorkerPool {
constructor(script, size = 4) {
this.queue = [];
this.workers = Array(size).fill(0).map(() => new Worker(script));
}
runTask(payload) {
const freeWorker = this.workers.pop();
if (freeWorker) {
freeWorker.postMessage(payload);
this.workers.push(freeWorker);
} else {
this.queue.push(payload);
}
}
}
</code></pre>
<h3>2. Zero-Copy Data Transfer</h3>
<p>Use <strong>Transferable Objects</strong> for massive data arrays.</p>
<pre><code>const buffer = new ArrayBuffer(50 * 1024 * 1024); // 50MB
worker.postMessage(buffer, [buffer]);
</code></pre>
<h3>3. Graceful Error Handling</h3>
<pre><code>worker.onerror = (err) => {
console.error("Worker error:", err);
fallbackToMainThread();
};
</code></pre>
<p>When to Use Web Workers</p>
<p><strong>Best suited for:</strong> - Big data filtering & sorting</p>
<ul>
<li>Image / video processing</li>
<li>Real-time analytics</li>
<li>Encryption or AI inference</li>
</ul>
<p><strong>Avoid for:</strong> - Simple DOM manipulation</p>
<ul>
<li>Small synchronous logic</li>
<li>Tasks needing frequent communication</li>
</ul>
<p>Conclusion</p>
<p>Before rewriting your app or migrating frameworks, ask:
<strong>"Can I fix this with Web Workers?"</strong></p>
<p>In most cases, the answer is yes.
This underrated API can transform performance with minimal code changes.
It's the smart, modern way to bring <strong>parallelism</strong> to the browser and
give users a truly smooth experience.</p>
<p>Web Workers embody the essence of smart optimization --- doing <strong>less on
the main thread</strong> and <strong>more in parallel</strong>.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/howto/basic-webpack-project/">Basic Webpack 5 Project with CSS: Step-by-Step</a></li>
<li><a href="/howto/build-first-ai-app-openai-api/">Complete Beginner's Guide to Creating AI Applications with OpenAI</a></li>
<li><a href="/howto/bun-install-vs-bun-add/">Bun Install vs Bun Add: Choosing the Right Command</a></li>
</ul>
Friday Links #31 — JavaScript Trends and Tools (Oct 31, 2025)https://jsdev.space/friday/friday-31/https://jsdev.space/friday/friday-31/Discover the latest JavaScript tools, libraries, and development trends from October 2025 — including new frameworks, libs, and AI-powered dev tools."Fri, 31 Oct 2025 00:00:00 GMT<p>Welcome to Friday Links #31, your weekly roundup of the freshest updates in the JavaScript world. From emerging frameworks to powerful dev utilities and clever performance hacks, this week’s list spotlights tools that can make your workflow smoother and your apps faster. Whether you’re building front-end interfaces, optimizing bundles, or experimenting with AI integrations — there’s something here to inspire your next project.</p>
<p><img src="./images/friday-31.png" alt="Friday Links #31" /></p>
<h2>DeepSeek V3.1 Tops AI Stock Trading Challenge — With Some Fine Print</h2>
<p>A new open benchmark called <strong>AI-Trader</strong> is testing how well AI models can handle real-world stock trading. Each model starts with <strong>$10,000</strong> and trades Nasdaq-100 stocks using real-time prices and financial news.</p>
<p>After the first month, <strong>DeepSeek v3.1</strong> leads with a <strong>+16.46%</strong> return, followed by <strong>MiniMax-M2 (+12.03%)</strong>, <strong>GPT-5 (+9.98%)</strong>, and <strong>Claude 3.7 Sonnet (+9.8%)</strong>.<br />
Meanwhile, <strong>Qwen3-max</strong> earned <strong>+7.96%</strong>, and <strong>Gemini-2.5-Flash</strong> barely moved the needle at <strong>+0.48%</strong> despite the most trades.<br />
For comparison, a passive <strong>QQQ ETF</strong> tracker gained <strong>+5.39%</strong>, proving DeepSeek’s picks weren’t just lucky — they were smart.</p>
<p><img src="./images/AI-Stock-Trading-Challenge.png" alt="AI Stock Trading Challenge" /></p>
<hr />
<h3>Important Context</h3>
<p>Still, the experiment comes with caveats:</p>
<ul>
<li><strong>Simulated setup</strong> – models trade at the <strong>day’s opening price</strong>, not in real markets.</li>
<li><strong>Possible data leaks</strong> – some news lacked timestamps, letting models see “future” info.</li>
<li><strong>Short, bullish window</strong> – tech stocks like Nvidia and Microsoft dominated, inflating returns.</li>
</ul>
<p>DeepSeek shows it can <strong>spot patterns and take confident bets</strong>, but calling it the best AI trader yet would be premature.</p>
<p>If the AI-Trader team adds <strong>hour-level trading</strong> and fixes data issues, it could become a reliable benchmark for evaluating how AIs handle <strong>risk</strong>, <strong>discipline</strong>, and <strong>adaptability</strong> in real financial markets.</p>
<hr />
<h2>📜 Articles & Tutorials</h2>
<p><a href="https://www.databricks.com/blog/build-high-quality-domain-specific-agents-95-lower-cost">Build High-Quality, Domain-Specific Agents at 95% Lower Cost</a></p>
<p><a href="https://jovidecroock.com/blog/state-vs-signals/">State-based vs Signal-based rendering</a></p>
<p><a href="https://www.nan.fyi/database">Build Your Own Database</a></p>
<p><a href="https://tkdodo.eu/blog/context-inheritance-in-tan-stack-router">Context Inheritance in TanStack Router</a></p>
<p><a href="https://blog.curbanii.net/practical-caching-recipes-for-next-js-app-router/">Practical Caching Recipes for Next.js (App Router)</a></p>
<p><a href="https://css-tip.com/conditional-border-radius/">Conditional Border Radius with Modern CSS</a></p>
<p><a href="https://news.opensuse.org/2025/10/08/gsoc-semantic-video-search/">GSoC 2025, Building a Semantic Search Engine for Any Video</a></p>
<p><a href="https://tanstack.com/blog/directives-and-the-platform-boundary">Tanstack Directives and the Platform Boundary</a></p>
<p><a href="https://appwrite.io/blog/post/why-developers-leaving-nextjs-tanstack-start">Why developers are leaving Next.js for TanStack Start, and loving it</a></p>
<p><a href="https://www.howtogeek.com/i-run-a-full-linux-desktop-in-docker-just-because-i-can/">I Run a Full Linux Desktop in Docker Just Because I Can</a></p>
<h2>⚒️ Tools</h2>
<p><a href="https://vscode-extension.vercel.app/">VSCode Vercel Extention</a></p>
<p><a href="https://jj-vcs.github.io/jj/latest/">Jujutsu</a> — a version control system</p>
<p><a href="https://github.com/vercel/workflow">Workflow DevKit</a> - A toolkit for building Vercel Workflow integrations</p>
<p><a href="https://github.com/jasonjmcghee/WebMCP">WebMCP</a> - WebMCP allows a website to be an MCP server. No sharing API Keys. Use any model you want.</p>
<p><a href="https://www.delopsu.com/draw">ASCII Drawing Board</a></p>
<p><a href="https://github.com/honojs/cli">Hono CLI</a> - Command Line Interface for Hono framework</p>
<p><a href="https://typingsvg.vercel.app/">Typing SVG Generator</a></p>
<p><a href="https://oklch.fyi/">Explore color as we see it</a> - A tool to visualize colors in the Oklch color space</p>
<p><a href="https://github.com/imputnet/helium">Helium</a> - Private, fast, and honest web browser</p>
<p><a href="https://handy.computer/">Handy</a> - the free and open source app for speech to text</p>
<h2>📚 Libs</h2>
<p><a href="https://github.com/mountain-loop/yaak">Yaak</a> - The most intuitive desktop API client. Organize and execute REST, GraphQL, WebSockets, Server Sent Events, and gRPC 🦬</p>
<p><a href="https://github.com/sindresorhus/ky">Ky</a> - 🌳 Tiny & elegant JavaScript HTTP client based on the Fetch API</p>
<p><a href="https://github.com/qgis/QGIS">QGIS</a> - QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)</p>
<p><a href="https://github.com/character-ai/Ovi">Ovi</a> - Twin Backbone Cross-Modal Fusion for Audio-Video Generation</p>
<h2>⌚ Releases</h2>
<p><a href="https://vitest.dev/blog/vitest-4">Vitest 4.0 is out!</a></p>
<p><a href="https://nextjs.org/blog/next-16">Next.js 16 Released</a></p>
<p><a href="https://biomejs.dev/blog/biome-v2-3/">Biome v2.3 Released</a></p>
<p><a href="https://bun.com/blog/bun-v1.3.1">Bun v1.3.1 Released</a></p>
<p><a href="https://boajs.dev/blog/2025/10/22/boa-release-21">Boa release v0.21</a></p>
<p><a href="https://eslint.org/blog/2025/10/eslint-v9.38.0-released/">ESLint v9.38.0 released</a></p>
<p><a href="https://github.com/web-infra-dev/rspack/releases/tag/v1.6.0-beta.1">Rspack 1.6.0 Beta 1</a></p>
<p><a href="https://github.com/storybookjs/storybook/releases/tag/v10.0.0">Storybook 10.0 Released</a> — Major Update Adds Module Automocking, Next.js 16, Vitest 4, and Async Svelte Component Support</p>
<p><a href="https://github.com/shadcn-ui/ui/releases/tag/shadcn%403.5.0">shadcn/ui 3.5 Released</a> — Now Fully Compatible with Next.js 16</p>
<p><a href="https://pnpm.io/blog/releases/10.20">pnpm 10.20</a>, <a href="https://astro.build/blog/astro-5150/">Astro 5.15</a>, <a href="https://github.com/ali-master/uuidv47/releases/tag/1.2.0">uuidv47 v1.2.0</a></p>
<h2>📺 Videos</h2>
<p><a href="https://www.youtube.com/watch?v=NPMi-3uf_ig">Is this the end for Adobe?</a></p>
<p><a href="https://www.youtube.com/watch?v=R4oYppOBKwo">The coolest feature of Hono</a></p>
<p><a href="https://www.youtube.com/watch?v=I1V9YWqRIeI">Next.js 16 Full Course | Build and Deploy a Production-Ready Full Stack App</a></p>
<p><a href="https://www.youtube.com/watch?v=gjrXeqgxbas">I Will Never Use Shadcn Form Components Again</a></p>
<p><a href="https://www.youtube.com/watch?v=HIp8sFB2GGw">Cursor 2.0 is here... 5 things you didn't know it can do</a></p>
<p><a href="https://www.youtube.com/watch?v=yl0YWA2K2B0">React wants to win you back…</a></p>
<p><a href="https://www.youtube.com/watch?v=KI4gjUrOfOs">Master React 19.2 Async Everywhere — Boost Performance</a></p>
<p><a href="https://www.youtube.com/watch?v=O8uazkirvVo">WebAssembly might actually save web dev...</a></p>
<p><a href="https://www.youtube.com/watch?v=G1XiiXTQHSE">React Native Tutorial for Absolute Beginners - Build a Mobile App in 2 Hours</a></p>
<p><a href="https://www.youtube.com/watch?v=P8rrhZTPEAQ">99% of Developers Don't Get PostgreSQL</a></p>
<p><a href="https://www.youtube.com/watch?v=8U2FNq2-IiE">Build Docker Images in a Git Repo but Only Committed Changes</a></p>
<p><a href="https://www.youtube.com/watch?v=v9gsMFozXK0">The Standup - Jira Bought 2 Browsers???</a></p>
<h2>🎤 Talks & Podcasts</h2>
<…Zip-bombs vs Aggressive AI Crawlers: Defensive Tactics for Siteshttps://jsdev.space/zip-bombs/https://jsdev.space/zip-bombs/How site owners are using decomposition-based payloads and other tactics to defend against aggressive AI crawlers that create excessive load.Thu, 30 Oct 2025 00:00:00 GMT<p>Some site owners are reporting huge traffic spikes caused by crawlers, especially crawlers targeting web content for LLMs and RAG (retrieval-augmented generation). Fastly's analytics indicate that scrapers and fetchers can create peaks of up to <strong>40,000 requests per minute</strong>. In 2025 scraper traffic rose roughly <strong>87%</strong>, driven mostly by RAG-style scrapers rather than primary model training.</p>
<p><img src="./images/zip-bomb-code-snippet.png" alt="Zip bomb code snippet" title="A code snippet generating a zip bomb" /></p>
<p>Today AI crawlers produce about <strong>80% of AI-bot traffic</strong> on the web; the remaining 20% are fetchers that cause intense short‑term spikes. For example, ClaudeBot (Anthropic) reportedly sent 1M requests in a day to iFixit.com and 3.5M requests in four hours to Freelancer.com.</p>
<p><img src="./images/Growth-of-AI-Crawler-Traffic.png" alt="Growth of AI Crawler Traffic (Fastly Report)" title="Growth of AI Crawler Traffic (Fastly Report)" /></p>
<p>One of the most persistent bots among all is Facebook’s crawler.</p>
<p><iframe width="560" height="315" src="https://www.youtube.com/embed/_N3l6h7bTWU?si=f2c1oUj_OE5zGM2K" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p>
<p>This bot periodically changes its user-agent after previous ones are blocked.</p>
<p>Perplexity AI crawlers have also been observed operating outside their official IP ranges and ignoring the <code>robots.txt</code> directives.</p>
<p><img src="./images/Perplexity-robots-txt.png" alt="Perplexity AI Crawler Ignoring robots.txt" title="Perplexity AI Crawler Ignoring robots.txt" /></p>
<h2>Recognizing the problem</h2>
<p>Common signs of problematic crawlers:</p>
<ul>
<li>High sustained request rates (thousands per minute).</li>
<li>Rotating user‑agents after blocks.</li>
<li>Requests coming from unexpected IP ranges or ignoring <code>robots.txt</code>.</li>
<li>Requests that refuse compressed content (a potential sign of zip‑bomb probing).</li>
</ul>
<p>Traditional defenses include rate limiting, CAPTCHAs, user‑agent filtering, IP reputation checks, and behavioural heuristics. There are also more aggressive or creative options — some administrators aim to make scraping expensive for the crawler operator rather than for themselves.</p>
<h2>Proof-of-work challenges (client puzzles)</h2>
<p>Some systems force clients to perform small computational puzzles, similar to Hashcash. The server issues a challenge; the client must return a value whose hash matches a difficulty rule (leading zeroes, for example). A Go‑style pseudo example:</p>
<pre><code>calcString := fmt.Sprintf("%s%d", challenge, nonce)
calculated := internal.SHA256sum(calcString)
if subtle.ConstantTimeCompare([]byte(response), []byte(calculated)) != 1 {
// reject
}
// compare the leading zeroes
if !strings.HasPrefix(response, strings.Repeat("0", rule.Challenge.Difficulty)) {
// reject
}
</code></pre>
<p>The idea is to raise the compute or bandwidth cost for the crawler infrastructure. These puzzles are inspired by the old anti-spam system <strong>Hashcash</strong>. While they may deter casual crawlers, they are not foolproof and can be circumvented by determined actors or distributed infrastructures.</p>
<p>It’s believed that <a href="https://github.com/TecharoHQ/anubis">Anubis</a> creates a noticeable computational load on AI data centers running crawlers. While it may not fully block their access, it effectively increases operational costs and slows down large-scale scraping attempts.</p>
<p>The project has earned over 14,100 stars on GitHub
, which indirectly reflects the number of websites using it for protection and its growing popularity among developers.</p>
<p>However, <a href="https://lock.cmpxchg8b.com/anubis.html">critics argue</a> that the additional load is relatively insignificant for large-scale infrastructures and that the protection mechanism can be easily bypassed with distributed or adaptive crawler networks.</p>
<p><img src="./images/anubis.png" alt="Anubis" title="Anubis" /></p>
<h2>Fingerprinting and heuristics</h2>
<p>Fingerprinting techniques can help identify non‑standard crawlers: odd user‑agent strings, refusal to accept compressed responses, unusual request patterns, or lack of human‑like JavaScript/browser behaviour. Fingerprinting is useful for routing suspicious traffic into specialized handling (throttling, challenge pages, or outright blocks).</p>
<h2>Zip‑bombs as a defensive tactic</h2>
<p>A <strong>zip‑bomb</strong> is a compressed payload crafted to expand massively when decompressed, exhausting CPU, memory, or disk on the client side. Site owners have started serving intentionally dense or highly repetitive content to make scraping expensive for crawlers that automatically decompress archives or HTML.</p>
<p>Examples used defensively include a 10 MB gzip that expands to ~10 GB of content:</p>
<pre><code>$ dd if=/dev/zero bs=1M count=10240 | gzip -9 > 10G.gzip
</code></pre>
<p>Another example builds a valid but hugely repetitive HTML document (shell-ish pseudocode):</p>
<pre><code>#!/bin/fish
# Base HTML
echo -n '<!DOCTYPE html><html lang=en><head><meta charset=utf-8><title>Valid HTML bomb</title>...</head><body><!--'
# create chunk file
echo -n (string repeat --count 258 'H') >/tmp/H_258
# repeat the chunk many times
for i in (seq 507)
cat (yes /tmp/H_258 | head --lines=81925)
end
cat (yes /tmp/H_258 | head --lines=81924)
# close comment and body
echo -n '--><p>This is a valid HTML bomb</p></body></html>'
# compress
$ fish zip_bomb.fish | gzip -9 > bomb.html.gz
$ du -sb bomb.html.gz
10180 bomb.html.gz
</code></pre>
<p>Because well‑behaved crawlers obey <code>robots.txt</code>, pages containing such payloads are normally disallowed. But crawlers that ignore <code>robots.txt</code> — including some AI crawlers — may still fetch them. Note that compressing repetitive content can reach extreme ratios (reported 1:1030 in some cases).</p>
<p>Some administrators create HTML bombs by streaming repeated tags; this can be done like:</p>
<pre><code>(echo '<html><head></head><body>' && yes "<div>") | dd bs=1M count=10240 iflag=fullblock | gzip > bomb.html.gz
</code></pre>
<p><strong>Important:</strong> these payloads can also break normal browsers and tools, so they must be placed carefully and never linked from public pages where real users might reach them by accident.</p>
<p>Although such protection methods can harm the overall health of the web, some webmasters still use them when bots generate more than <strong>50% of total server load</strong>.</p>
<p>While older search engine crawlers typically respect <code>robots.txt</code> directives, newer <strong>AI crawlers</strong> often act far more destructively. For instance, one webmaster shared analytics charts showing how <strong><a href="https://openai.com/gptbot">GPTBot</a></strong> from OpenAI <a href="https://www.reddit.com/r/CloudFlare/comments/1jp8mv8/do_turn_on_block_ai_bots_or_make_a_robotstxt_if/">consumed over 30 TB of traffic</a> within a single month — meaning his entire website, totaling about <strong>600 MB</strong>, was downloaded nearly <strong>50,000 times</strong>.</p>
<p><img src="./images/web-traffic-graph.png" alt="Web Traffic Graph" title="Web Traffic Graph" /></p>
<h2>Practical considerations and ethics</h2>
<ul>
<li><strong>Effectiveness varies.</strong> Modern crawlers may bypass simple protections by ignoring compressed payloads, skipping disallowed paths, or parallelising decompression across many nodes. Researchers have shown that some defensive payloads are easy to work around.</li>
<li><strong>Collateral damage.</strong> Zip‑bom…Using URLPattern as a Framework-Free Router in Node 24https://jsdev.space/urlpattern-router-node24/https://jsdev.space/urlpattern-router-node24/Learn how to use the native URLPattern API as a lightweight, framework-free router in both browsers and Node.js 24 for unified client-server routing.Tue, 28 Oct 2025 00:00:00 GMT<p>JavaScript finally got a <strong>native routing primitive</strong> that works the same way in both the <strong>browser</strong> and <strong>Node.js 24</strong>. The <code>URLPattern</code> API can now act as a <strong>framework-free router</strong>, handling route matching with named groups, strict validation, and consistent semantics everywhere.</p>
<p>In Node 24, <code>URLPattern</code> is available <strong>globally</strong> (no imports). You can define patterns once and reuse them in Service Workers or HTTP servers, all without external dependencies.</p>
<hr />
<h2>1. The Concept</h2>
<p><code>URLPattern</code> matches URLs by parts — <code>protocol</code>, <code>hostname</code>, <code>port</code>, <code>pathname</code>, <code>search</code>, and <code>hash</code>.<br />
It supports <strong>named groups</strong>, <strong>case sensitivity</strong>, and a <strong>clean pattern syntax</strong>.</p>
<p>Examples:</p>
<pre><code>// Named parameter with validation
new URLPattern({ pathname: '/users/:id([0-9]+)' });
// Optional subdomain
new URLPattern({ hostname: '{:sub.}?api.example.com' });
// Relative path resolved against a base
new URLPattern('../admin/*', 'https://example.com/app/');
</code></pre>
<p>Browser support is strong (Chrome, Firefox, Safari, Edge), and in Node 24 it’s built-in.</p>
<hr />
<h2>2. A Mini Router with URLPattern</h2>
<p>Let’s build a tiny, type-safe router that works in <strong>Node</strong> and <strong>Service Workers</strong> — no frameworks, no globals, no dependencies.</p>
<pre><code>// mini-router.js
export class PathRule {
constructor({ method = 'GET', pattern, base, opts, handler, cast = {} }) {
if (!pattern) throw new TypeError('Missing URL pattern');
this.method = method.toUpperCase();
this.pattern =
typeof pattern === 'string'
? new URLPattern(pattern, base, opts)
: new URLPattern(pattern, opts);
this.handler = handler;
this.cast = cast; // { key: fn(string) => any }
}
match(url, base) {
if (!this.pattern.test(url, base)) return null;
const result = this.pattern.exec(url, base);
const params = Object.assign(
{},
result.protocol?.groups,
result.hostname?.groups,
result.port?.groups,
result.pathname?.groups,
result.search?.groups,
result.hash?.groups
);
for (const [k, fn] of Object.entries(this.cast)) {
if (params[k] != null) {
const val = fn(params[k]);
if (val === undefined) return null;
params[k] = val;
}
}
return { params, result };
}
}
export class SmartRouter {
constructor({ base } = {}) {
this.routes = [];
this.base = base;
}
register(ruleDef) {
const rule = new PathRule(ruleDef);
this.routes.push(rule);
return this;
}
find({ url, method = 'GET' }) {
const href = typeof url === 'string' ? url : url.href;
const m = method.toUpperCase();
for (const route of this.routes) {
if (route.method !== m) continue;
const match = route.match(href);
if (match) return { route, ...match };
}
return null;
}
}
</code></pre>
<hr />
<h2>3. Declaring Routes</h2>
<p>Let’s define a small router with type-safe parameters.</p>
<pre><code>// routes.js
import { SmartRouter } from './mini-router.js';
export function initRouter() {
const r = new SmartRouter();
// GET /health
r.register({
method: 'GET',
pattern: { pathname: '/health' },
handler: async () => new Response('ok', { status: 200 }),
});
// GET /users/:id
r.register({
method: 'GET',
pattern: { pathname: '/users/:id([1-9][0-9]*)' },
cast: {
id: s => {
const n = Number(s);
return Number.isSafeInteger(n) ? n : undefined;
},
},
handler: async ({ params }) => toJSON({ id: params.id }),
});
// GET /search?q&limit
r.register({
method: 'GET',
pattern: { pathname: '/search', search: '?q=:q&limit=:limit([0-9]{1,2})' },
cast: {
limit: s => {
const n = Number(s);
return n >= 1 && n <= 50 ? n : 10;
},
},
handler: async ({ url, params }) => {
const q = params.q ?? url.searchParams.get('q') ?? '';
const limit = params.limit ?? 10;
return toJSON({ q, limit });
},
});
return r;
}
function toJSON(obj, init = {}) {
return new Response(JSON.stringify(obj), {
headers: { 'content-type': 'application/json; charset=utf-8' },
...init,
});
}
</code></pre>
<hr />
<h2>4. Using It in a Service Worker</h2>
<pre><code>// worker.js
import { initRouter } from './routes.js';
const router = initRouter();
self.addEventListener('fetch', e => {
const req = e.request;
const url = new URL(req.url);
if (url.origin !== self.location.origin) return;
const found = router.find({ url, method: req.method });
if (!found) return;
e.respondWith(processRequest(found, req, url));
});
async function processRequest(found, req, url) {
try {
const ctx = { request: req, url, params: found.params, match: found.result };
const res = await found.route.handler(ctx);
return sanitizeHeaders(res);
} catch {
return new Response('internal error', { status: 500 });
}
}
function sanitizeHeaders(res) {
const h = new Headers(res.headers);
if (!h.has('cache-control')) h.set('cache-control', 'no-store');
return new Response(res.body, { status: res.status, headers: h });
}
</code></pre>
<hr />
<h2>5. Using It in Node.js 24</h2>
<pre><code>// server.js
import http from 'node:http';
import { initRouter } from './routes.js';
const router = initRouter();
const BASE = process.env.BASE_ORIGIN || 'http://localhost';
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, BASE);
const found = router.find({ url, method: req.method });
if (!found) {
res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
res.end('not found');
return;
}
try {
const ctx = { request: req, url, params: found.params, match: found.result };
const webRes = await found.route.handler(ctx);
await respondNode(res, webRes);
} catch {
res.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' });
res.end('internal error');
}
});
server.listen(3000, () => console.log('Server on http://localhost:3000'));
async function respondNode(res, webResponse) {
const buffer = Buffer.from(await webResponse.arrayBuffer());
const headers = Object.fromEntries(webResponse.headers.entries());
res.writeHead(webResponse.status, headers);
res.end(buffer);
}
</code></pre>
<hr />
<h2>6. Performance and Security Notes</h2>
<ul>
<li><strong>Precompile once</strong>: reuse <code>URLPattern</code> instances instead of creating them per request.</li>
<li><strong>Avoid unsafe regex input</strong>: do not accept user-supplied groups.</li>
<li><strong>Enable <code>ignoreCase</code></strong> only where appropriate.</li>
<li><strong>In Node</strong>, always build absolute URLs — relative paths are invalid without a base origin.</li>
<li><strong>In Service Workers</strong>, match only your own origin.</li>
</ul>
<p><code>URLPattern</code> may not beat optimized routers like <code>find-my-way</code> in raw performance, but it’s ideal for <strong>shared client-server logic</strong> and <strong>unified validation</strong>.</p>
<hr />
<h2>7. Example: Matching Hosts and Protocols</h2>
<pre><code>const proto = new URLPattern({ protocol: 'http{s}?' });
const host = new URLPattern({ hostname: '{:sub.}?example.com' });
const assets = new URLPattern({ pathname: '/static/*' });
const user = new URLPattern({ pathname: '/users/:id([1-9][0-9]*)' });
const search = new URLPattern({
pathname: '/search',
search: '?q=:q&limit=:limit([0-9]{1,2})',
});
</code></pre>
<hr />
<h2>8. Benchmark: URLPattern vs RegExp</h2>
<pre><code>// bench.js
const N = Number(process.env.N || 1_000_000);
const samples = [
'http://localhost/users/1?active=1',
…The Hidden Power of return in JavaScript Constructorshttps://jsdev.space/js-constructor-return/https://jsdev.space/js-constructor-return/Discover when returning a value from a JavaScript constructor actually makes sense — and how it can produce self-constructing functions and inheritance patternsFri, 24 Oct 2025 00:00:00 GMT<p>Most JavaScript developers learn that constructors shouldn’t explicitly return anything. After all, constructors are supposed to create and initialize instances — not hand back arbitrary objects. But there’s a curious exception that’s both legal and powerful: <strong>a constructor can return its own function or class.</strong></p>
<p>Let’s explore why this behavior exists, what it enables, and when it’s the only possible way to achieve a certain pattern in JavaScript.</p>
<h2>The Basics: What Constructors Usually Return</h2>
<p>By default, when you call a function with <code>new</code>, JavaScript implicitly returns the newly created object bound to <code>this</code>.
If the constructor returns <strong>an object</strong>, that object replaces the implicit one.
If it returns <strong>a primitive</strong>, JavaScript ignores it and still returns <code>this</code>.</p>
<pre><code>function Example() {
this.value = 42;
return 7; // ignored
}
const instance = new Example();
console.log(instance.value); // 42
</code></pre>
<p>Only object returns matter — and that opens a subtle door.</p>
<h2>The Rare Case: Returning a Function or Class</h2>
<p>Functions and classes in JavaScript are objects, so they can be <strong>returned</strong> from constructors.
That means a constructor can itself <strong>produce a constructible function</strong> — an instance that can later be called with <code>new</code> again.</p>
<pre><code>function FactoryConstructor() {
const Inner = function () {
console.log("Inner constructor called!");
};
// Inherit from the outer constructor
Object.setPrototypeOf(Inner, this);
return Inner;
}
const Custom = new FactoryConstructor();
console.log(Custom instanceof FactoryConstructor); // true
const innerInstance = new Custom(); // Works!
</code></pre>
<p>This creates a fascinating effect:
The <strong>instance of the constructor</strong> is itself a <strong>constructor</strong>.</p>
<p>You can now chain <code>new</code> twice — a concept rarely seen in real-world code but perfectly valid.</p>
<h2>Why It Works</h2>
<p><code>Object.setPrototypeOf(Inner, this)</code> connects the inner function’s prototype chain to the original constructor’s prototype.
Without this line, <code>instanceof</code> checks would fail.</p>
<p>You can think of it like this:</p>
<ul>
<li>The first <code>new</code> creates a <strong>function object</strong> (constructor).</li>
<li>The second <code>new</code> creates an <strong>instance of that returned function</strong>.</li>
</ul>
<p>This makes the returned function an extension of the original constructor.</p>
<h2>What If We Try Using Proxy Instead?</h2>
<p>Some developers might attempt to use <code>Proxy</code> to simulate this dual-constructor behavior. Unfortunately, it doesn’t work:</p>
<pre><code>"use strict";
function Ctor() {
const proxy = new Proxy(this, {
construct(target, args) {
return new target(...args);
}
});
return proxy;
}
const inst = new Ctor();
console.log(inst instanceof Ctor); // true
try {
new inst();
} catch (err) {
console.error(err.message);
// TypeError: inst is not a constructor
}
</code></pre>
<p>Even with clever proxy traps, JavaScript doesn’t allow an ordinary instance to suddenly become constructible — unless it’s explicitly returned as a <strong>function or class</strong> object.</p>
<h2>Real-World Application</h2>
<p>This pattern might seem academic, but it has a few niche uses:</p>
<ol>
<li>
<p><strong>Dynamic Class Generation:</strong>
Creating specialized subclasses on demand that share a common prototype.</p>
<pre><code>function ClassBuilder(name) {
return class {
constructor() {
this.name = name;
}
};
}
const UserClass = new ClassBuilder("User");
const user = new UserClass();
console.log(user.name); // "User"
</code></pre>
</li>
<li>
<p><strong>Self-constructing Factories:</strong>
Functions that produce new constructors with pre-bound behavior.</p>
<pre><code>function Service(type) {
const SubService = function (name) {
this.type = type;
this.name = name;
};
Object.setPrototypeOf(SubService, this);
return SubService;
}
const Logger = new Service("logger");
const fileLogger = new Logger("FileLogger");
console.log(fileLogger instanceof Logger); // true
console.log(fileLogger.type); // "logger"
</code></pre>
</li>
</ol>
<h2>TypeScript Considerations</h2>
<p>TypeScript doesn’t natively expect constructors to return something other than their instance type.
To make this pattern type-safe, you can define a generic return type:</p>
<pre><code>function Builder<T extends object>(): new (...args: any[]) => T {
const Inner = function () {} as any;
return Inner;
}
</code></pre>
<p>It’s unusual but possible to express, and may be useful in advanced metaprogramming scenarios.</p>
<h2>Key Takeaways</h2>
<ul>
<li>Returning a <strong>non-primitive</strong> value from a constructor replaces the default <code>this</code>.</li>
<li>Returning a <strong>function or class</strong> turns your constructor into a <strong>constructor factory</strong>.</li>
<li><code>Object.setPrototypeOf()</code> can preserve prototype inheritance and <code>instanceof</code> checks.</li>
<li>This is the <strong>only</strong> way to make an instance that’s also constructible.</li>
</ul>
<h2>Final Thoughts</h2>
<p>Most developers will never need this pattern — and that’s okay. But understanding it deepens your grasp of JavaScript’s object model and reveals just how flexible the language really is.</p>
<p>Sometimes the best way to learn is to find that one “impossible” question — and realize JavaScript already has a quirky, elegant answer.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/algorithm-complexity-big-o/">Understanding Big O Notation with JavaScript</a></li>
<li><a href="/animejs-animation-guide/">Mastering Web Animations with Anime.js</a></li>
<li><a href="/await-fetch-slow/">Optimizing await fetch(): Why It Slows Down and How to Speed It Up</a></li>
</ul>
Logical Assignment Operators in JavaScript: Cleaner, Smarter Codehttps://jsdev.space/logical-assignment-operators-js/https://jsdev.space/logical-assignment-operators-js/Discover how JavaScript’s logical assignment operators (||=, &&=, ??=) simplify conditional logic, reduce boilerplate, and make your code easier to read.Fri, 24 Oct 2025 00:00:00 GMT<p>When working with JavaScript, we often check whether a variable has a value before assigning something new. These repetitive checks — especially when dealing with props, configuration objects, or component state — can clutter your code.</p>
<p>That’s where <strong>logical assignment operators</strong> come in. Introduced in <strong>ES2021</strong>, they allow you to perform conditional assignments in a concise and expressive way without changing the logic of your program.</p>
<p>If you want to learn more about safe assignment syntax in JavaScript, check out this related guide:<br />
<a href="https://jsdev.space/safe-assignment-operator/">Safe Assignment Operator in JavaScript →</a></p>
<h2>What Are Logical Assignment Operators?</h2>
<p>Logical assignment operators combine standard logical operators (||, &&, ??) with the assignment operator (=):</p>
<ul>
<li>||= — assign if the left-hand side is falsy</li>
<li>&&= — assign if the left-hand side is truthy</li>
<li>??= — assign if the left-hand side is null or undefined</li>
</ul>
<p>They work similarly to normal logical expressions: the right-hand side is evaluated <strong>only if needed</strong>.</p>
<blockquote>
<p>Note: The optional chaining operator (?.) cannot be used on the left side of logical assignment. Doing so will cause a SyntaxError.</p>
</blockquote>
<h2>1. OR Assignment (||=)</h2>
<p>Assigns a value only if the variable is falsy (false, 0, "", null, undefined, or NaN):</p>
<pre><code>user.theme ||= 'light';
</code></pre>
<p>Equivalent to:</p>
<pre><code>if (!user.theme) {
user.theme = 'light';
}
</code></pre>
<p>This is perfect for providing <strong>default values</strong>, but be cautious — values like 0, "", or false will be overwritten.</p>
<h2>2. AND Assignment (&&=)</h2>
<p>Assigns a value only if the variable is truthy:</p>
<pre><code>user.isLoggedIn &&= checkPermissions(user);
</code></pre>
<p>Equivalent to:</p>
<pre><code>if (user.isLoggedIn) {
user.isLoggedIn = checkPermissions(user);
}
</code></pre>
<p>Be aware that the right-hand expression is <strong>always assigned</strong>, even if it evaluates to false:</p>
<pre><code>let isEnabled = true;
isEnabled &&= false;
console.log(isEnabled); // false
</code></pre>
<h2>3. Nullish Assignment (??=)</h2>
<p>Assigns a value only if the variable is null or undefined:</p>
<pre><code>settings.timeout ??= 3000;
</code></pre>
<p>Equivalent to:</p>
<pre><code>if (settings.timeout === null || settings.timeout === undefined) {
settings.timeout = 3000;
}
</code></pre>
<p>Unlike ||=, this preserves valid falsy values like 0, false, or "".</p>
<h2>Using Logical Assignment with Component Props</h2>
<p>These operators shine in component-based frameworks like React or Vue, where props often need default values:</p>
<pre><code>props.title ||= 'Untitled';
props.visible ??= true;
props.theme &&= props.theme.toLowerCase();
</code></pre>
<p>Benefits:</p>
<ul>
<li>Cleaner, shorter code</li>
<li>Fewer if or ternary expressions</li>
<li>More predictable handling of null, undefined, or falsy values</li>
</ul>
<p>Examples:</p>
<pre><code>props.showHelpText ??= true; // Default value
config.apiBase ||= '/api/v1'; // Only set if not defined
formData.username &&= formData.username.trim(); // Update if exists
</code></pre>
<h2>Things to Watch Out For</h2>
<h3>1. ||= Overwrites Falsy Values</h3>
<pre><code>let count = 0;
count ||= 10; // count becomes 10 — unexpected!
</code></pre>
<p>Use ??= if you want to preserve falsy but valid values.</p>
<h3>2. Lazy Evaluation Prevents Side Effects</h3>
<pre><code>config.apiKey ||= fetchApiKey();
// fetchApiKey() runs only if apiKey is falsy
</code></pre>
<h2>Example with Side Effects</h2>
<pre><code>let calls = 0;
let obj = { val: 0 };
obj.val ||= ++calls;
console.log(obj.val); // 1
obj.val ||= ++calls;
console.log(obj.val); // still 1
</code></pre>
<p>Explanation:</p>
<ul>
<li>Initially, obj.val is 0 (falsy), so ++calls runs and assigns 1.</li>
<li>On the second line, obj.val is now truthy, so ++calls isn’t evaluated again.</li>
</ul>
<h2>Browser Support</h2>
<p>Chrome 85+, Firefox 79+, Safari 14+, Edge 85+<br />
Node.js 15+<br />
Internet Explorer — not supported</p>
<p>If you need compatibility with older environments, use a transpiler like Babel with @babel/preset-env targeting ES2021.</p>
<h2>Final Thoughts</h2>
<p>Logical assignment operators may seem like small additions, but they <strong>make your JavaScript cleaner, more expressive, and easier to maintain</strong>.</p>
<p>They’re especially useful when:</p>
<ul>
<li>Setting <strong>default props or state</strong></li>
<li>Working with <strong>configuration objects</strong></li>
<li>Writing <strong>form validation or cleanup logic</strong></li>
</ul>
<p>Once you start using ||=, &&=, and ??=, you’ll wonder how you ever wrote code without them.</p>
<h2>Related articles</h2>
<ul>
<li><a href="/algorithm-complexity-big-o/">Understanding Big O Notation with JavaScript</a></li>
<li><a href="/animejs-animation-guide/">Mastering Web Animations with Anime.js</a></li>
<li><a href="/await-fetch-slow/">Optimizing await fetch(): Why It Slows Down and How to Speed It Up</a></li>
</ul>