<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex</title>
    <description>The latest articles on DEV Community by Alex (@asmyshlyaev177).</description>
    <link>https://dev.to/asmyshlyaev177</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F186893%2F2f9e908a-4e24-4761-a5c5-4c39003c4c8a.gif</url>
      <title>DEV Community: Alex</title>
      <link>https://dev.to/asmyshlyaev177</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/asmyshlyaev177"/>
    <language>en</language>
    <item>
      <title>I built llm-queue: one local model, one queue</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Mon, 29 Jun 2026 09:44:15 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/i-built-llm-queue-one-local-model-one-queue-15l4</link>
      <guid>https://dev.to/asmyshlyaev177/i-built-llm-queue-one-local-model-one-queue-15l4</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEFORE  two processes, two private queues, one small GPU

  jobbot       ─┐
                ├──▶  Ollama
  slop filter  ─┘

  both hit the model at once → reload thrash, ~4x slower


AFTER  one shared queue over HTTP, in front of one model

  jobbot       ─┐
                ├─ HTTP ─▶  llm-queue ─ fetch ─▶  Ollama
  slop filter  ─┘

  one request at a time → the model loads once and stays put
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have two side projects that both lean on a local LLM, and for a while they kept making each other slow. Fixing that turned into a small package called &lt;strong&gt;llm-queue&lt;/strong&gt;: one process that owns the model and runs every request, from every program, through a single serialized priority queue behind an OpenAI-compatible HTTP API.&lt;/p&gt;

&lt;p&gt;The two projects are jobbot, a bot that scrapes job boards and classifies each posting with a local model (is this actually a job? is it remote? does it want a language I don't speak?), and a browser extension that hides LinkedIn feed slop the same way. One's a Node cron job, the other's an MV3 service worker. Both reach for the same Ollama model on my laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: one model, one small GPU
&lt;/h2&gt;

&lt;p&gt;Two facts make concurrency a bad bet here. A local model answers one request at a time, so parallel callers don't actually run in parallel. And my GPU has 6 GB, which barely fits a single model, so there's no room to load a second one beside it.&lt;/p&gt;

&lt;p&gt;So the obvious plan, let both projects run and hit Ollama whenever they want, fell apart fast. When two processes hit it at once with different models, or even the same model at a different context size, Ollama unloads one and loads the other on every call. That reload is gigabytes off disk, and it turns out to be most of your wall-clock time. I benchmarked it to be sure: throughput dropped to about a quarter of what a single client got. Concurrency made things slower. About four times slower.&lt;/p&gt;

&lt;p&gt;Pinning both models in memory doesn't save you either. They don't fit, so they spill onto the CPU and fight over cores, and now both are slow. On this hardware, nothing beats one request at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: one shared queue
&lt;/h2&gt;

&lt;p&gt;The answer is boring. Don't run requests in parallel. Put them in one line and serve them one at a time. The line just has to be global, shared across every process, and the one thing all my programs already speak is HTTP.&lt;/p&gt;

&lt;p&gt;So llm-queue is a single process that owns the Ollama endpoint and the model. Everything else talks to it over HTTP. One queue, one model, one request in flight, ever, so the model loads once and stays put.&lt;/p&gt;

&lt;p&gt;There's one wrinkle: not every request is equal. jobbot fires off dozens of background classifications while it scrapes, but when I click a job to draft an application, I'm waiting on that one answer right now. So it's a priority queue. A request marked priority jumps the waiting backlog, though it never interrupts whatever's already running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it speak OpenAI
&lt;/h2&gt;

&lt;p&gt;I didn't want to ship a client library. Everything already speaks the OpenAI API, so I made the service speak it too. Point any OpenAI client at &lt;code&gt;http://localhost:11500/v1&lt;/code&gt; and you get the queue for free, no SDK swap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:11500/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unused&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;granite4.1:8b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two knobs the queue needs don't exist in the OpenAI shape, so they ride along as extra body fields. &lt;code&gt;priority&lt;/code&gt; is the line-jump from above. &lt;code&gt;numCtx&lt;/code&gt; is the context window a client wants; the service runs at the largest size any client asks for and never shrinks back, so the model doesn't reload to a smaller window mid-day. Stock OpenAI servers ignore fields they don't recognize, so the exact same request still works against the real API. It just drops the two extras.&lt;/p&gt;

&lt;p&gt;The service also hands back the model's raw string and nothing else. No JSON parsing, no schema validation. My two consumers parse the output differently, and every time I pictured "helping" them inside the queue, I was really just guessing wrong on their behalf. The queue does the one job only it can do: serialize access, with a per-attempt timeout and one retry. The rest is the caller's problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two projects, one queue, no shared code
&lt;/h2&gt;

&lt;p&gt;The part I like most is that neither consumer depends on the package. jobbot doesn't &lt;code&gt;npm install llm-queue&lt;/code&gt;. Neither does the extension. They just POST to the running service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:11500/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pageText&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;response_format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;json_object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// ask for JSON&lt;/span&gt;
    &lt;span class="na"&gt;numCtx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                             &lt;span class="c1"&gt;// llm-queue extension&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;choices&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole integration. The extension, running in a completely separate browser, sends its own classifications to the same service. (Browsers block a content script from calling localhost, so that fetch lives in the service worker, and the service sends permissive CORS headers so it goes through.) The two projects have never heard of each other. They don't share a queue in code. They share one in &lt;em&gt;production&lt;/em&gt;, because there is exactly one and it's reachable over HTTP. On a 6 GB GPU that's the difference between a model that loads once and one that thrashes all day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building it
&lt;/h2&gt;

&lt;p&gt;It's small on purpose: about 700 lines across the queue, the HTTP server, the fetch transport, and the CLI. TypeScript, ESM, Node 20+, almost no dependencies, talking to Ollama over plain &lt;code&gt;fetch&lt;/code&gt;. tsup builds it, vitest covers the queue and the server, and release-please turns my conventional commits into the version bump, changelog, and npm publish on merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you run a local model and more than one thing wants to use it, this is the missing piece:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; llm-queue
&lt;span class="nv"&gt;OLLAMA_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;granite4.1:8b llm-queue serve   &lt;span class="c"&gt;# → http://127.0.0.1:11500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then point anything OpenAI-shaped at &lt;code&gt;http://localhost:11500/v1&lt;/code&gt;, or just &lt;code&gt;fetch&lt;/code&gt; it directly. It's on &lt;a href="https://www.npmjs.com/package/llm-queue" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, the source is on &lt;a href="https://github.com/asmyshlyaev177/llm-queue" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, MIT licensed. If it saves your local model from thrashing the way it saved mine, a star on the repo is the easiest way to help the next person find it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Testing Next.js SSR with Playwright: record real responses instead of writing mocks</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 18 Jun 2026 11:56:04 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/testing-nextjs-ssr-with-playwright-record-real-responses-instead-of-writing-mocks-fk5</link>
      <guid>https://dev.to/asmyshlyaev177/testing-nextjs-ssr-with-playwright-record-real-responses-instead-of-writing-mocks-fk5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqac7h0fbnrdz7hpyyrph.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqac7h0fbnrdz7hpyyrph.gif" alt="Recording real API responses, then replaying them on CI with the backend turned off" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you write Playwright tests for a Next.js app, you've probably hit this wall. You reach for &lt;code&gt;page.route()&lt;/code&gt; to stub an API call, the test still fails, and after twenty minutes you realize the request you're trying to mock never went through the browser at all. Your server component fetched it during SSR. Playwright never saw it, because Playwright drives a browser, and that fetch happened in Node.&lt;/p&gt;

&lt;p&gt;This is the gap nobody upstream has closed cleanly. &lt;code&gt;routeFromHAR&lt;/code&gt; is browser only. The Next.js team has an &lt;a href="https://github.com/vercel/next.js/discussions/67136" rel="noopener noreferrer"&gt;open discussion about mocking server actions in e2e tests&lt;/a&gt; and &lt;a href="https://github.com/vercel/next.js/discussions/76436" rel="noopener noreferrer"&gt;another about middleware&lt;/a&gt;, both still unresolved. There's a &lt;a href="https://github.com/microsoft/playwright/issues/4928" rel="noopener noreferrer"&gt;four-year-old Playwright feature request&lt;/a&gt; asking for exactly the thing you want: record network requests, save them as ready-to-use mocks. Still open.&lt;/p&gt;

&lt;p&gt;So people work around it. The current options are all variations on the same idea: hand-write the mocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hand-written mock treadmill
&lt;/h2&gt;

&lt;p&gt;MSW is the popular answer. It intercepts in both the browser and Node, and since Next.js 15 it can finally see server-side calls. But you still author every handler by hand, keep its shape in sync with the real API, and update it when the backend changes. MSW also keeps its state globally, so running tests in parallel gets fiddly fast. Mocky Balboa, the experimental Next.js test proxy, &lt;code&gt;request-mocking-protocol&lt;/code&gt;, they all land in the same place. You're maintaining a second, fake copy of your API forever.&lt;/p&gt;

&lt;p&gt;I did this for a while and got tired of it. The mocks drift. The real API adds a field, your mock doesn't have it, the test passes anyway, and you ship a bug that only shows up against production data. The whole point of an e2e test is to run against something that behaves like the real thing, and a mock I typed out by hand at 2am is the opposite of that.&lt;/p&gt;

&lt;p&gt;What I actually wanted was the VCR pattern. Ruby had this nailed years ago: hit the real API once, save the interaction to a file, replay it from disk on every run after that. The JavaScript version of this was Polly.js from Netflix, but it's effectively unmaintained now (last meaningful push was a while back), and it never had a clean story for SSR plus Playwright anyway. The throne was empty. So I built &lt;strong&gt;test-proxy-recorder&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: record once, replay forever
&lt;/h2&gt;

&lt;p&gt;The model is simple. There are two modes.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;record&lt;/strong&gt; mode, your app talks to the real backend through a proxy. The proxy forwards everything and writes each request and response to disk as it goes.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;replay&lt;/strong&gt; mode, the proxy serves those saved responses straight from disk. No backend, no network, same bytes every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                        Record mode                          Replay mode

  Browser/App ──&amp;gt; Proxy ──&amp;gt; Real API        Browser/App ──&amp;gt; Proxy ──&amp;gt; Disk
                    │                                         │
                    └──&amp;gt; saves to disk                        └──&amp;gt; serves saved responses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trick for SSR is that it's a proxy, not a browser hook. Your Next.js server fetches go through it just like browser fetches do, so it captures both. Browser-side requests get recorded separately as HAR files (the format Playwright already understands), and server-side requests land in &lt;code&gt;.mock.json&lt;/code&gt; files. You don't think about which is which day to day, you just record and commit.&lt;/p&gt;

&lt;p&gt;The thirty-second version of the pitch: record your tests against the real API, delete the backend, run the tests again, watch them pass. There's a short video of exactly that on the site, &lt;a href="https://test-proxy-recorder.dev" rel="noopener noreferrer"&gt;test-proxy-recorder.dev&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it into Next.js
&lt;/h2&gt;

&lt;p&gt;The only genuinely Next-specific part is letting the proxy know which test a server-side fetch belongs to. The browser request carries a header that ties it to the running test; you forward that same header on your SSR fetches. A tiny middleware does it for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// proxy.ts  (Next.js 16 middleware convention)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setNextProxyHeaders&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-proxy-recorder/nextjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;setNextProxyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// no-op in production&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/((?!_next/static|_next/image|favicon.ico).*)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a no-op in production, so it's safe to leave in. On Next.js 15 and earlier the file is &lt;code&gt;middleware.ts&lt;/code&gt; with the function named &lt;code&gt;middleware&lt;/code&gt;, otherwise identical.&lt;/p&gt;

&lt;p&gt;Then point your API base URL env var at the proxy in dev and test only, and write a normal Playwright test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playwrightProxy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-proxy-recorder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Flip to 'record' to hit the real API and refresh recordings.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MODE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;replay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;playwrightProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/localhost:8100/&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;homepage loads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record once with &lt;code&gt;MODE = 'record'&lt;/code&gt;, switch it back to &lt;code&gt;'replay'&lt;/code&gt;, commit the &lt;code&gt;e2e/recordings/&lt;/code&gt; folder, and CI never touches the network again. If the setup above looks like a few too many moving parts, &lt;code&gt;npx test-proxy-recorder init&lt;/code&gt; scaffolds the config, the fixture, the teardown, and the package scripts in one shot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts that were actually hard
&lt;/h2&gt;

&lt;p&gt;The recording loop is the easy 80%. The interesting problems are the reasons people abandon record-replay tools, so most of my time went there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets.&lt;/strong&gt; A record-replay tool quietly tells you to commit real API responses to git, which means it's one missed review away from committing a live token. So redaction is on by default now. &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Cookie&lt;/code&gt;, and &lt;code&gt;Set-Cookie&lt;/code&gt; get stripped before anything hits disk, and you can add your own header names or body regexes for API keys. It's safe because replay matching ignores those headers anyway, so scrubbing them never breaks playback. For login itself, the recommended pattern keeps the whole auth flow out of recordings: run it in a Playwright setup project with the proxy in transparent mode and persist &lt;code&gt;storageState&lt;/code&gt; to a gitignored file. There's a working example against a real AWS Cognito pool in the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebSockets.&lt;/strong&gt; This one surprised me. Recording a socket is fine, but how do you replay it? Real time? Then a test that watches a price ticker takes as long as the recording did. So the default replays server messages as a burst on connect, which is fast and deterministic, since tests usually assert on the state that arrived, not the timing. If you do care about timing, &lt;code&gt;--ws-timing original&lt;/code&gt; re-paces the messages from their recorded timestamps. I validated it against Binance's live BTC-USD feed: record the real stream once, replay deterministic prices on CI with no exchange account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-deterministic requests.&lt;/strong&gt; Timestamps, UUIDs, CSRF tokens in the URL. This is the classic record-replay killer, and I'll be honest, it's the area still getting work. Matching is currently method plus path plus a hash of the query string, and configurable matchers for volatile params are the next thing on the list. If your URLs are stable, you won't notice. If they're full of cache-busters, that's the rough edge today.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this is the wrong tool
&lt;/h2&gt;

&lt;p&gt;It's not a unit-test mock. If you want to assert "the UI shows an error when the API returns 500," hand-writing that one response with &lt;code&gt;page.route()&lt;/code&gt; is faster and clearer. test-proxy-recorder earns its keep when you have real, complicated API responses you don't want to reproduce by hand, especially across SSR and browser and sockets in the same test run. It also assumes you can reach the real backend at least once to record. Fully air-gapped from day one, it can't help you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; test-proxy-recorder
npx test-proxy-recorder init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site is &lt;a href="https://test-proxy-recorder.dev" rel="noopener noreferrer"&gt;test-proxy-recorder.dev&lt;/a&gt;, and the repo is &lt;a href="https://github.com/asmyshlyaev177/test-proxy-recorder" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; with runnable examples for Next.js 16, a Chrome extension hitting X's API, the crypto ticker, and a real-auth Cognito app. MIT licensed.&lt;/p&gt;

&lt;p&gt;If you've been fighting Playwright over SSR mocking, give it a run against your own API and tell me where it breaks. Open an issue, send a PR, or come ask in the &lt;a href="https://discord.gg/w7rgYbY5zz" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; if you get stuck wiring it up. The honest edges above are exactly the kind of feedback that moves the roadmap.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>nextjs</category>
      <category>apimocking</category>
    </item>
    <item>
      <title>Attempt to stop npm postinstall scripts from stealing your secrets</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 14 May 2026 13:18:36 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/attempt-to-stop-npm-postinstall-scripts-from-stealing-your-secrets-3adp</link>
      <guid>https://dev.to/asmyshlyaev177/attempt-to-stop-npm-postinstall-scripts-from-stealing-your-secrets-3adp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqy7fe7eb3tijq9f05k0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqy7fe7eb3tijq9f05k0h.png" alt="ringfence"&gt;&lt;/a&gt;&lt;br&gt;
Every time you run &lt;code&gt;npm install&lt;/code&gt;, you're rolling dice. A postinstall script from some dependency three layers deep gets unrestricted access to your filesystem. &lt;code&gt;.env&lt;/code&gt; files, SSH keys, cloud credentials, your whole &lt;code&gt;$HOME&lt;/code&gt; directory. It can read it all, and it can ship it anywhere because the network is wide open.&lt;/p&gt;

&lt;p&gt;In May 2026, a supply chain worm compromised 84 npm packages that had valid SLSA Build Level 3 provenance. Postinstall scripts siphoned CI/CD secrets, SSH keys, cloud credentials, crypto wallets. It spread quietly because everything looked legitimate. The attack got nicknamed "Shai-Hulud" after the Dune sandworm that swallows everything in its path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ringfence&lt;/strong&gt; blocks this class of attack at the OS level. Zero config. It wraps your package manager commands in a sandbox so a compromised dependency can't see anything worth stealing.&lt;/p&gt;

&lt;p&gt;One thing to be clear about: ringfence doesn't stop you from installing malicious code. If a package is bad, it still gets installed. What ringfence does is make sure that the postinstall script running on your machine can't read your secrets, can't touch your SSH keys, can't sniff your &lt;code&gt;.env&lt;/code&gt;. It sits between the compromised package and &lt;em&gt;your&lt;/em&gt; filesystem, not between you and the registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Ringfence is basically a shim. It drops a handful of shell scripts at &lt;code&gt;~/.ringfence/bin/&lt;/code&gt; and shoves that directory onto the front of your &lt;code&gt;PATH&lt;/code&gt;. When you type &lt;code&gt;npm install&lt;/code&gt;, the shim catches it and asks: does this command trigger lifecycle scripts? If yes, it goes through the sandbox. If it's &lt;code&gt;npm run build&lt;/code&gt; or &lt;code&gt;npm test&lt;/code&gt; or &lt;code&gt;npx tsc&lt;/code&gt;, it passes straight through to the real binary. You pay about 80ms of Node startup overhead either way.&lt;/p&gt;

&lt;p&gt;On Linux it leans on bubblewrap. The sandbox replaces &lt;code&gt;$HOME&lt;/code&gt; with an empty tmpfs. After that it selectively brings back only what the package manager needs: &lt;code&gt;.npm&lt;/code&gt;, pnpm's store directories, yarn's cache, and registry auth files like &lt;code&gt;.npmrc&lt;/code&gt; (those are mounted read only). &lt;code&gt;~/.ssh&lt;/code&gt;, &lt;code&gt;~/.aws&lt;/code&gt;, &lt;code&gt;~/.gnupg&lt;/code&gt; stay invisible because they were never added to the blank tmpfs in the first place.&lt;/p&gt;

&lt;p&gt;Next, every secret looking file in your project directory gets masked with &lt;code&gt;/dev/null&lt;/code&gt;. &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.env.*&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, &lt;code&gt;id_rsa*&lt;/code&gt;, &lt;code&gt;credentials.json&lt;/code&gt;, service account JSON files, and a bunch of other patterns. The files are there, they're just zero bytes.&lt;/p&gt;

&lt;p&gt;Environment variables that smell like secrets get unset: anything with &lt;code&gt;TOKEN&lt;/code&gt;, &lt;code&gt;SECRET&lt;/code&gt;, &lt;code&gt;API_KEY&lt;/code&gt;, or &lt;code&gt;PASSWORD&lt;/code&gt; in the name, plus prefixes like &lt;code&gt;AWS_&lt;/code&gt;, &lt;code&gt;GITHUB_&lt;/code&gt;, &lt;code&gt;NPM_&lt;/code&gt;, &lt;code&gt;DATABASE_&lt;/code&gt;. The network stays open. Registries, git deps, tarball URLs all work fine. The defense isn't blocking exfiltration. It's making sure there's nothing to exfiltrate.&lt;/p&gt;

&lt;p&gt;On macOS there's no bubblewrap (no Linux namespaces), so ringfence falls back to Docker. The project directory gets copied to a temp location with all secret files filtered out, mounted into an ephemeral &lt;code&gt;node:lts&lt;/code&gt; container with &lt;code&gt;HOME=/work&lt;/code&gt;, and after the install finishes only the non-secret files sync back. It's slower than the Linux path, but the security model is the same.&lt;/p&gt;

&lt;p&gt;The test suite includes a leak probe: a synthetic malicious script that tries reading &lt;code&gt;.env&lt;/code&gt; files, SSH keys, &lt;code&gt;~/.aws/&lt;/code&gt; credentials, &lt;code&gt;~/.gnupg/&lt;/code&gt; keys, and secret shaped env vars. Under ringfence, every single probe fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; ringfence &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npx ringfence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two commands. It detects your OS, installs bubblewrap if you're on Linux (using whatever package manager your distro ships), validates Docker if you're on macOS, creates shims for npm/pnpm/yarn/bun, and adds itself to your shell's &lt;code&gt;PATH&lt;/code&gt; via &lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.zshrc&lt;/code&gt;, and &lt;code&gt;.profile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that, every &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;pnpm add&lt;/code&gt;, &lt;code&gt;yarn add&lt;/code&gt;, or &lt;code&gt;bun add&lt;/code&gt; runs sandboxed. You don't set anything up per project. Commands like &lt;code&gt;npm run dev&lt;/code&gt; and &lt;code&gt;npm test&lt;/code&gt; and &lt;code&gt;npx&lt;/code&gt; go through untouched, those aren't install commands.&lt;/p&gt;

&lt;p&gt;To get rid of it: &lt;code&gt;npx ringfence-uninstall&lt;/code&gt;. Cleans up the &lt;code&gt;PATH&lt;/code&gt; entries and deletes &lt;code&gt;~/.ringfence/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stuff worth knowing
&lt;/h2&gt;

&lt;p&gt;Secret files become zero byte files, not missing files. Making a file absent risks breaking scripts that check for existence. An empty read is safer than &lt;code&gt;ENOENT&lt;/code&gt;. The tradeoff: if a dependency genuinely needs a &lt;code&gt;.env&lt;/code&gt; file with actual content during install, things break. The README is upfront about this.&lt;/p&gt;

&lt;p&gt;Ringfence unsets env vars and masks files. Your &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; doesn't leak through the environment just because it wasn't stored in a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;bwrap containers launched by ringfence include &lt;code&gt;--die-with-parent&lt;/code&gt;. If ringfence crashes, the sandboxed process dies too. No orphaned containers.&lt;/p&gt;

&lt;p&gt;On macOS, packages removed during a container run stick around in &lt;code&gt;node_modules&lt;/code&gt; until you clean them manually. The sync back doesn't delete files, it only adds or overwrites. Uninstalled packages linger.&lt;/p&gt;

&lt;p&gt;Ringfence only protects install time, when lifecycle scripts fire. Once packages are on disk, &lt;code&gt;npm start&lt;/code&gt; or &lt;code&gt;node server.js&lt;/code&gt; runs with full host access. That's intentional. It's a supply chain guard, not a runtime sandbox.&lt;/p&gt;

&lt;p&gt;No Windows support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother
&lt;/h2&gt;

&lt;p&gt;The TanStack worm hit packages that had provenance. SLSA Build Level 3, verified. Provenance says where code came from, not what it does. That's the whole problem.&lt;/p&gt;

&lt;p&gt;Ringfence is two commands and covers npm, pnpm, yarn, and bun on Linux and macOS. If you're installing dependencies on either platform, there's not much reason to skip it.&lt;/p&gt;




&lt;p&gt;If you give ringfence a try, I'd love to hear how it goes. The macOS path in particular could use more real world testing, so feedback from Mac users is especially welcome. Bug reports, edge cases, or just "works fine on my machine" — all useful. Drop an issue on &lt;a href="https://github.com/asmyshlyaev177/ringfence/issues" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you give ringfence a try, I'd love to hear how it goes. The macOS path in particular could use more real world testing, so feedback from Mac users is especially welcome. Bug reports, edge cases, or just "works fine on my machine" — all useful. Drop an issue on &lt;a href="https://github.com/asmyshlyaev177/ringfence/issues" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ringfence.pages.dev/" rel="noopener noreferrer"&gt;https://ringfence.pages.dev/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>npm</category>
      <category>supplychainattack</category>
      <category>shaihulud</category>
    </item>
    <item>
      <title>How I Built state-in-url: My Journey Turning the URL Bar into Real React State</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Mon, 13 Apr 2026 13:39:54 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/how-i-built-state-in-url-my-journey-turning-the-url-bar-into-real-react-state-a67</link>
      <guid>https://dev.to/asmyshlyaev177/how-i-built-state-in-url-my-journey-turning-the-url-bar-into-real-react-state-a67</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24etxlwjhdvzujmmb2yb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24etxlwjhdvzujmmb2yb.gif" alt="state-in-url demo gif" width="600" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm the developer behind &lt;strong&gt;state-in-url&lt;/strong&gt;. The library lets you put real JavaScript values - numbers, Date objects, nested arrays, objects - directly into browser query parameters. It keeps full TypeScript support throughout. The hook behaves exactly like useState, but the state lives in the URL. It survives reloads. You can share the link and nothing gets lost.&lt;/p&gt;

&lt;p&gt;Try the live demo at &lt;a href="https://state-in-url.dev" rel="noopener noreferrer"&gt;https://state-in-url.dev&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Type in the form. Watch the URL change instantly. Refresh the page. The data stays exactly as you left it. The example sits right in the repo too.&lt;/p&gt;

&lt;p&gt;This project grew from a frustration I hit again and again while working on dashboards and larger forms. State ended up split across local variables, a store, and the URL for sharing. Other libraries came close, yet they always added too much setup or turned everything into strings and dropped my types. I decided to build the version I actually wanted to use every day.&lt;/p&gt;

&lt;p&gt;The repo now sits at 400+ stars on GitHub. Version 6.1.2 shipped a few weeks ago. It still surprises me that people use it. Here is the full story - the frustrations, the choices that worked, the testing struggles, and what stayed with me afterward.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You have probably faced this situation. A page holds filters or a detailed form. State lives in three places at once. Local component values. Maybe a store. Plus the URL so users can bookmark or share their exact view. Syncing it all felt like constant extra work.&lt;/p&gt;

&lt;p&gt;I wanted the hook to stay dead simple. Return state and a setter, just like useState, but with TypeScript awareness of the exact shape. Real data types that survive the URL round trip. Clean support for Next.js App Router with SSR, plus React Router and Remix. No interference with other query parameters. And keep the whole thing tiny - zero runtime dependencies, under 2 KB gzipped.&lt;br&gt;
That became my starting point in spare time.&lt;/p&gt;
&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;The choice I remain happiest about is straightforward. I used one plain JavaScript object for both default values and the type definition. Without separate schema or heavy generics. You write code like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;agree_to_terms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}[],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FormState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass that object to the hook and everything flows. Types and defaults work out of the box.&lt;br&gt;
For shared state with useSharedState, I relied on a single JavaScript object. Components subscribe to changes, without React Context. It handled cases where unrelated parts of the app needed the same data without added complexity.&lt;br&gt;
The encoder and decoder required some tuning. The goal was a readable URL that still preserved real types. Encoding a larger object takes roughly a millisecond on my machine.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the Library and the Framework Challenges
&lt;/h2&gt;

&lt;p&gt;I structured the project as a pnpm monorepo. TypeScript dominates the codebase. Core logic lives in one package. Thin adapters cover Next.js, React Router, and Remix. Rollup manages the build. Nothing overly complicated.&lt;br&gt;
Next.js App Router created the biggest difficulties. The way searchParams moves between server and client components caused repeated issues. I added a small header workaround to carry data through on the server side. Hydration stayed intact. Remix and React Router presented fewer obstacles, yet I still needed to handle history modes, push versus replace, and debounce settings so the URL stayed calm during typing.&lt;/p&gt;

&lt;p&gt;Here is a real section from the Next.js 15 demo that runs live at state-in-url.dev&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useUrlState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;state-in-url/next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;formState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./formState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;urlState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useUrlState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
       &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;urlState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;curr&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="nx"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}))}&lt;/span&gt;
       &lt;span class="nx"&gt;onBlur&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="c1"&gt;// update URL on blur for smoother typing&lt;/span&gt;
     &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* tags, dates, checkboxes - all typed correctly */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code stays simple. It is just state that happens to live in the URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing was hard but important
&lt;/h2&gt;

&lt;p&gt;I needed the library to feel reliable across frameworks and browsers. Real example apps for Next.js 14+, React Router v6+, and Remix live in the repo. Vitest covers the encoder and decoder basics. More real world tests cases covered with Playwright running full end-to-end tests in Chrome, Firefox, and Safari.&lt;/p&gt;

&lt;p&gt;A fix in one place often broke something elsewhere. The commit message "Tests finally passing on Next.js 15" appeared more times than I care to count. It took quite few attempts adjusting Playwright configurations and the CI matrix.&lt;br&gt;
GitHub Actions now runs linting, building, unit tests, and the complete browser suite on every push and PR. Semantic-release automates version bumps, changelog updates, GitHub releases, and npm publishing. Husky keeps commits tidy. Once the pipeline ran smoothly, maintaining the library stopped feeling like a burden.&lt;/p&gt;
&lt;h2&gt;
  
  
  Launching
&lt;/h2&gt;

&lt;p&gt;I published to npm. The README received every recipe I could gather - debounced updates, resetting to defaults, multiple independent state objects on one page. I linked the live demo and waited.&lt;br&gt;
The first stars arrived and felt surprisingly satisfying. Issues opened, mostly kind ones. Solid CI let me ship fixes quickly. I wrote the honest limits in a separate Limits.md file. URLs hold only so much - around 12 KB stays practical. Readers seemed to value the directness.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Making the API match useState exactly shaped every other decision and made the library pleasant to use.&lt;/li&gt;
&lt;li&gt;That single object for shape and defaults removed a lot of complexity.&lt;/li&gt;
&lt;li&gt;Testing inside real frameworks felt painful yet essential - skipping it would have limited adoption.&lt;/li&gt;
&lt;li&gt;Automating releases and chores with semantic-release and GitHub Actions turned maintenance into something manageable.&lt;/li&gt;
&lt;li&gt;Keeping the bundle tiny happened on purpose. I watched the size and avoided dependencies deliberately.
Would I change anything today? I might add Svelte or Astro support earlier. Hash routing could be interesting. The roadmap still has room to grow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this sound interesting give it a try&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add state-in-url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo lives at &lt;a href="https://state-in-url.dev" rel="noopener noreferrer"&gt;state-in-url.dev&lt;/a&gt;. The repository sits &lt;a href="https://github.com/asmyshlyaev177/state-in-url" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Star it if the approach fits your work.&lt;/p&gt;

&lt;p&gt;I would enjoy hearing about the URL-state problems you encounter. Leave a comment. Open an issue. Send a pull request. Sharing the process publicly became one of the most rewarding aspects.&lt;br&gt;
Happy coding, MIT license, of course. Build something useful with it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>statemanagement</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>New portfolio website</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Mon, 15 Sep 2025 15:24:22 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/new-portfolio-website-4o5i</link>
      <guid>https://dev.to/asmyshlyaev177/new-portfolio-website-4o5i</guid>
      <description>&lt;p&gt;Tried few new things this time.&lt;/p&gt;

&lt;p&gt;Created an initial design with V0, had to refactor a lot, and change many details.&lt;/p&gt;

&lt;p&gt;Chose an Astro framework for a minimal footprint on bundle size, and the best performance.&lt;/p&gt;

&lt;p&gt;It's quite simple, but hopefully worth it. Will appreciate any feedback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asmyshlyaev177.dev/" rel="noopener noreferrer"&gt;Portfolio link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>portfolio</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React rendering quiz</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 08 May 2025 09:55:24 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/react-rendering-quiz-443k</link>
      <guid>https://dev.to/asmyshlyaev177/react-rendering-quiz-443k</guid>
      <description>&lt;p&gt;A simple quiz to illustrate how React rendering works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      Parent component
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Child component&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to guess what will be printed to console.&lt;/p&gt;

&lt;p&gt;Answer&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcarx620tb7bru42a5jw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcarx620tb7bru42a5jw.png" alt="Answer" width="241" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some explanation:&lt;br&gt;
⇾ First &lt;code&gt;Parent&lt;/code&gt; is parsed and rendered, without &lt;code&gt;useEffect&lt;/code&gt;,&lt;br&gt;
⇾ after that &lt;code&gt;Child&lt;/code&gt;, when there is no more nested components is turn of async hooks&lt;br&gt;
⇾ React will run &lt;code&gt;useEffect&lt;/code&gt; from children to parents.&lt;/p&gt;

&lt;p&gt;Few more logs, probably related to dev mode.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Breakpoints variables with pure CSS</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 01 May 2025 10:28:18 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/breakpoints-variables-with-pure-css-18ln</link>
      <guid>https://dev.to/asmyshlyaev177/breakpoints-variables-with-pure-css-18ln</guid>
      <description>&lt;p&gt;In CSS can use variables most of the time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, can't use variables in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media" rel="noopener noreferrer"&gt;@media&lt;/a&gt; queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--br-md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c"&gt;/* will never work */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unexpected solution is to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@container" rel="noopener noreferrer"&gt;@container&lt;/a&gt; queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--is-tablet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;--is-tablet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--is-tablet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="s2"&gt;'header aside'&lt;/span&gt;
      &lt;span class="s2"&gt;'feed   aside'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7.16rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;17.6rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7.16rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max-content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--feed-columns&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only downside is Firefox lacks support of it, but it doesn't do so well on many things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js usage
&lt;/h2&gt;

&lt;p&gt;In Next.js can use &lt;a href="https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media" rel="noopener noreferrer"&gt;postcss-custom-media&lt;/a&gt; plugin to achieve same outcome, for all browsers and with css modules.&lt;/p&gt;

&lt;p&gt;How it looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@custom-media&lt;/span&gt; &lt;span class="n"&gt;--is-desktop&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--is-desktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="s2"&gt;'header aside'&lt;/span&gt;
      &lt;span class="s2"&gt;'feed   aside'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7.16rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;17.6rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7.16rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max-content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--feed-columns&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/pages/guides/post-css" rel="noopener noreferrer"&gt;Official&lt;/a&gt; docs&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install all dependencies:&lt;br&gt;
&lt;code&gt;npm install --save-dev postcss autoprefixer postcss-flexbugs-fixes @csstools/postcss-global-data postcss-custom-media postcss-preset-env&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add PostCSS config to &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"postcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"postcss-flexbugs-fixes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@csstools/postcss-global-data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;need&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;variables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"core/styles/vars.css"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"postcss-custom-media"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"postcss-preset-env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"autoprefixer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"flexbox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no-2009"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"custom-properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>css</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Minification bugs</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Fri, 04 Apr 2025 11:37:21 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/minification-bugs-2p9m</link>
      <guid>https://dev.to/asmyshlyaev177/minification-bugs-2p9m</guid>
      <description>&lt;p&gt;Today I learned that minification in JS is a double-edged sword.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhknespllmamj0mkhjjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhknespllmamj0mkhjjc.png" alt="Esbuild docs" width="799" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was a weird bug, took me quite some time to figure out that problem in minification.&lt;br&gt;
Stopped using &lt;code&gt;terser&lt;/code&gt; for the same reasons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyup1lsqr57tut71tv75e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyup1lsqr57tut71tv75e.png" alt="esbuild issue" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Next.js at refactor or extinct situation.</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Fri, 13 Dec 2024 08:21:03 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/nextjs-at-refactor-or-extinct-situation-27mi</link>
      <guid>https://dev.to/asmyshlyaev177/nextjs-at-refactor-or-extinct-situation-27mi</guid>
      <description>&lt;p&gt;Next.js become a hype of the day (or year), but it's slowly suffocating under the weight of technical debt.&lt;/p&gt;

&lt;p&gt;There are many big enough pain points with Next.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow and painful dev mode, if you changed something and need to check more than 1 route, you need to wait till route compiled. Next.js server also uses few gigabytes of RAM. Turbopack complaining about perfectly working TS code, and struggle to understand &lt;code&gt;:global&lt;/code&gt; in CSS modules, still not production ready.&lt;/li&gt;
&lt;li&gt;Whole workflow is intended to be used mostly with server components, but in real world applications big chunks of applications use client side JS, because it's way faster and more responsive.&lt;/li&gt;
&lt;li&gt;Routing is a mess, pages router was good enough, but App Router it's just a wrapper around it, that lead to more bugs and complexity.&lt;/li&gt;
&lt;li&gt;Vercel service is good, but if popularity of your project going parabolic, costs will follow - &lt;a href="https://news.ycombinator.com/item?id=40618220" rel="noopener noreferrer"&gt;96k bill&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just stumbled upon a bug when &lt;code&gt;useParams&lt;/code&gt; hook periodically returning dead wrong values.&lt;/p&gt;

&lt;p&gt;Searched for existing issues, didn't find any, tried to open issue on their GitHub, can't do than without providing reproducible example. Repository is private, I can't share all of it. And I can't pinpoint the exact condition for a bug to appear in a big enough codebase.&lt;/p&gt;

&lt;p&gt;Similar situation was with &lt;code&gt;Cypress&lt;/code&gt; testing framework. For years people opened issues about flaky tests and memory leaks, all of them were closed, because can't properly reproduce them. Easy to make something works in a TODO app, real challenge is a big and complex application. Usually, people work under NDA and can't just share the whole repository.&lt;br&gt;
After some time Playwright came and Cypress isn't relevant any more.&lt;/p&gt;

&lt;p&gt;Let's see approximate quality ratio, by calculating stars to bugs for Cypress.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3ygqalkolclgbro763r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3ygqalkolclgbro763r.png" alt="Cypress github stats" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;47600 stars / 14600 bugs = &lt;strong&gt;34 quality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cypress&lt;/code&gt;'s successor - &lt;code&gt;Playwright&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmueqcjz1w9aal975t1v0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmueqcjz1w9aal975t1v0.png" alt="Playwright github stats" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;67_700 stars / 692 issue = &lt;strong&gt;97 quality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm aware that many bugs issues it's just misunderstanding or duplicates, still, if something confusing in a documentation/example, it is a con of software.&lt;/p&gt;

&lt;p&gt;Let's see about &lt;code&gt;Next.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1mysu3v1goa6yughj4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1mysu3v1goa6yughj4z.png" alt="Next.js github stats" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;128_000 stars / 3000 bugs = &lt;strong&gt;42 quality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's see &lt;code&gt;React.js&lt;/code&gt; repo for context&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F597zqm0gu4y2s4o0rmhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F597zqm0gu4y2s4o0rmhr.png" alt="React.js github stats" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;230_000 stars / 754 bugs = &lt;strong&gt;305 quality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;Remix.js&lt;/code&gt; repo (SSR framework from &lt;code&gt;react-router&lt;/code&gt; team)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g5x88ve929x7892po87.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g5x88ve929x7892po87.png" alt="Remix.js github stats" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;30_300 stars / 295 bugs = &lt;strong&gt;102 quality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All of this just my opinions, but it's backed by years of experience with different frameworks/libraries and observations.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond console.log</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Sun, 24 Nov 2024 09:49:53 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/beyond-consolelog-1h40</link>
      <guid>https://dev.to/asmyshlyaev177/beyond-consolelog-1h40</guid>
      <description>&lt;h2&gt;
  
  
  intro
&lt;/h2&gt;

&lt;p&gt;As much as I hope that things will always work from first try, better to be prepared to debug it. &lt;code&gt;console.log&lt;/code&gt; remains the main tool to do it, because it is the simplest and fastest.&lt;/p&gt;

&lt;p&gt;Few console methods are known to everybody, &lt;code&gt;log&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going deeper
&lt;/h2&gt;

&lt;p&gt;There are more tricks to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;count&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For example, if you need to count how many times component re-render can use &lt;code&gt;console.count&lt;/code&gt;, e.g. &lt;code&gt;console.count('comp1')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furymtdynzh46ldlb96ga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furymtdynzh46ldlb96ga.png" alt="console.count" width="663" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;group&lt;/code&gt; and &lt;code&gt;groupCollapsed&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Can log some info in a collapsed group with &lt;code&gt;console.groupCollapsed&lt;/code&gt;, great in case of many error messages, when they printed collapsed it's less distracting, but Node.js/Sentry probably will print all expanded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjeclio3a0crk4gl59oby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjeclio3a0crk4gl59oby.png" alt="console.groupCollapsed" width="675" height="218"&gt;&lt;/a&gt;&lt;br&gt;
After click &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1dig323rwr0pmg998xh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1dig323rwr0pmg998xh.png" alt="console.groupCollapsed expanded" width="675" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  styling
&lt;/h3&gt;

&lt;p&gt;Can use some CSS&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv19au70e4gi4lpuyn9mt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv19au70e4gi4lpuyn9mt.png" alt="console.log with CSS styles" width="675" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;dir&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Can log properties of an object with &lt;code&gt;console.dir&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famahwwee45em2018xmuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famahwwee45em2018xmuv.png" alt="console.dir" width="408" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  objects
&lt;/h3&gt;

&lt;p&gt;Can log few objects with curly braces like &lt;code&gt;console.log({ obj1, obj2 })&lt;/code&gt;, instead of &lt;code&gt;console.log('Ojb1', obj1)&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;assert&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Can assert things with &lt;code&gt;console.assert&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4390c1ubs0a4yezgf501.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4390c1ubs0a4yezgf501.png" alt="console.assert" width="675" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;time&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Can measure how long some operation takes with &lt;code&gt;console.time&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhzjqa4estugyguph38r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhzjqa4estugyguph38r.png" alt="console.time" width="656" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;trace&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Print a stack trace with &lt;code&gt;console.trace()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq1o0nkxsawrssgb4mpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq1o0nkxsawrssgb4mpy.png" alt="console.trace" width="644" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;clear&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Clear things up with &lt;code&gt;console.clear()&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;Check out great old post - &lt;a href="https://dev.to/lissy93/fun-with-consolelog-3i59"&gt;https://dev.to/lissy93/fun-with-consolelog-3i59&lt;/a&gt; &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>First really useful AI tool - AI code review</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Thu, 14 Nov 2024 20:42:30 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/first-really-useful-ai-tool-ai-code-review-1an4</link>
      <guid>https://dev.to/asmyshlyaev177/first-really-useful-ai-tool-ai-code-review-1an4</guid>
      <description>&lt;p&gt;Finally, I found at least 1 really useful AI service.&lt;/p&gt;

&lt;h2&gt;
  
  
  The service
&lt;/h2&gt;

&lt;p&gt;It's called &lt;a href="https://www.coderabbit.ai/" rel="noopener noreferrer"&gt;&lt;strong&gt;CodeRabbit&lt;/strong&gt;&lt;/a&gt;, the idea behind it is pretty simple, AI review code changes and give you recommendations how to fix it with explanation.&lt;/p&gt;

&lt;p&gt;AI is working as pattern recognition, so, it's perfectly suitable for recognizing antipatterns in pull requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it looks like
&lt;/h2&gt;

&lt;p&gt;After PR created, GitHub action run and analyze changes. It's post summary like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2p6301wyhgsjs7qei327.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2p6301wyhgsjs7qei327.png" alt="PR review summary" width="800" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second comment contains recommendations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos86sp9ccna5faqpbj43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos86sp9ccna5faqpbj43.png" alt="PR review 1" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2pt8qk7jwuvtxayim08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2pt8qk7jwuvtxayim08.png" alt="PR review 2" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This service already found few minor mistakes that I missed. Definitely worth trying.&lt;/p&gt;

&lt;p&gt;Reviewing PRs is a very tedious task, AI can actually help with it.&lt;/p&gt;

&lt;p&gt;Of course there are probably 3 useful comments out of 10, but it is very similar with human reviewers, e.g. they're either nitpicking the every line or blindly approve huge PR.&lt;/p&gt;

&lt;p&gt;Treat such comments as possibly useful recommendation, not like obligation to fix everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Price
&lt;/h2&gt;

&lt;p&gt;Currently, you can use it for free with basic functionality, and 12$/month for Pro version.&lt;br&gt;
Probably, this promotion-like pricing will change in the future, but again, worth giving it a try.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>codereview</category>
    </item>
    <item>
      <title>Solving slow compilation in dev mode for Next.js</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Fri, 08 Nov 2024 12:04:05 +0000</pubDate>
      <link>https://dev.to/asmyshlyaev177/solving-slow-compilation-in-dev-mode-for-nextjs-3ilb</link>
      <guid>https://dev.to/asmyshlyaev177/solving-slow-compilation-in-dev-mode-for-nextjs-3ilb</guid>
      <description>&lt;p&gt;Noticed that &lt;code&gt;Next.js&lt;/code&gt; dev server is really slow, annoying that it can't precompile pages in the background.&lt;br&gt;
There is a &lt;a href="https://github.com/vercel/next.js/issues/48748" rel="noopener noreferrer"&gt;discussion about it.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ways to make it faster
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Turbopack
&lt;/h3&gt;

&lt;p&gt;Can use turbopack, can run it with &lt;code&gt;next dev --turbo&lt;/code&gt;, downsides that  if you use some fine tweaks for Webpack, not everything works correctly with turbopack (at least for v14).&lt;/p&gt;
&lt;h3&gt;
  
  
  Upgrade to Next.js v15
&lt;/h3&gt;

&lt;p&gt;v15 is faster by default, some common issues were solved. There is a &lt;a href="https://nextjs.org/docs/app/building-your-application/upgrading/version-15" rel="noopener noreferrer"&gt;codemod&lt;/a&gt; that will do most of the things for you in a few seconds.&lt;br&gt;
Still, requires some effort.&lt;/p&gt;
&lt;h3&gt;
  
  
  Config tweaks
&lt;/h3&gt;

&lt;p&gt;There is &lt;a href="https://nextjs.org/docs/app/api-reference/next-config-js/onDemandEntries" rel="noopener noreferrer"&gt;&lt;code&gt;onDemandEntries&lt;/code&gt;&lt;/a&gt; option for &lt;code&gt;next.config.js&lt;/code&gt;, can use it to force dev server to store more compiled pages in memory, so you will see fewer &lt;code&gt;compiling /page ...&lt;/code&gt; lines.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;onDemandEntries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// period (in ms) where the server will keep pages in the buffer&lt;/span&gt;
    &lt;span class="na"&gt;maxInactiveAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 15 minutes&lt;/span&gt;
    &lt;span class="c1"&gt;// number of pages that should be kept simultaneously without being disposed&lt;/span&gt;
    &lt;span class="na"&gt;pagesBufferLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tnx for reading.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
