<?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>Forem: vavilov2212</title>
    <description>The latest articles on Forem by vavilov2212 (@vavilov2212).</description>
    <link>https://forem.com/vavilov2212</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F927865%2F853c5ee9-29cb-4120-8e38-817c86623f59.jpg</url>
      <title>Forem: vavilov2212</title>
      <link>https://forem.com/vavilov2212</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vavilov2212"/>
    <language>en</language>
    <item>
      <title>🕵️‍♂️ The Case of the Missing S3 Metadata: A CORS Deep Dive</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Thu, 20 Nov 2025 21:15:09 +0000</pubDate>
      <link>https://forem.com/vavilov2212/the-case-of-the-missing-s3-metadata-a-cors-deep-dive-1gag</link>
      <guid>https://forem.com/vavilov2212/the-case-of-the-missing-s3-metadata-a-cors-deep-dive-1gag</guid>
      <description>&lt;p&gt;When you upload a file to S3 with custom metadata (like fileName or uploadedAt), everything looks correct in the AWS Console. But when you try to read that metadata from your frontend using &lt;code&gt;HeadObjectCommand&lt;/code&gt;, the data clearly exists on the cloud, yet the AWS SDK response returns &lt;em&gt;empty Metadata object&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There's a &lt;a href="https://github.com/aws/aws-sdk-js-v3/issues/3446" rel="noopener noreferrer"&gt;resolved GitHub issue&lt;/a&gt; on one of the JavaScript AWS packages.&lt;/p&gt;

&lt;p&gt;This is not an bug, the cause isn’t in the AWS SDK or your upload logic. It comes from the browser, and specifically how it handles cross-origin requests.&lt;/p&gt;

&lt;p&gt;The rest of this article breaks down where this behavior comes from and how to fix it.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;If you read my earlier article, &lt;a href="https://dev.to/vavilov2212/stop-uploading-files-to-s3-the-wrong-way-50le"&gt;“Stop Uploading Files to S3 the Wrong Way”&lt;/a&gt;, you saw a complete example of how to upload and download files efficiently using the AWS SDK with a client/server flow.&lt;/p&gt;

&lt;p&gt;This article builds directly on top of that and acts as a follow-up for developers searching for “client-side S3 metadata not showing” issues.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This is Part 2 in a small S3 series&lt;/li&gt;
&lt;li&gt;Client and Server behave differently&lt;/li&gt;
&lt;li&gt;Why no Metadata on the Client&lt;/li&gt;
&lt;li&gt;How to Fix It with S3 CORS Rules&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  This is Part 2 in a small S3 series
&lt;/h2&gt;

&lt;p&gt;In the first part, the focus was sending and retrieving files. Here, we look at reading custom metadata from a cloud S3 provider in the browser.&lt;/p&gt;

&lt;p&gt;You don’t need to read Part 1 to follow this article, but both posts fit together if you’re building a file upload pipeline end-to-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client and Server behave differently
&lt;/h2&gt;

&lt;p&gt;The issue only shows up in certain execution environments, so it helps to look at how the same code behaves in your backend (when it runs on the server) versus in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-side (✅ works as expected)
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;HeadObjectCommand&lt;/code&gt; runs in a nodejs backend environment - for example, a Next.js API route - it returns the full metadata without any issues. Server-to-server requests aren’t limited by browser security rules, so all response headers are available.&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;// src/app/api/s3/private/meta/route.ts&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;s3Service&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;../../_lib&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;const&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&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;req&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="o"&gt;=&amp;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;documentKey&lt;/span&gt; &lt;span class="o"&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;nextUrl&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;metadata&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;s3Service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;documentKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&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;
  
  
  Client-side (❌ metadata comes back empty)
&lt;/h3&gt;

&lt;p&gt;The problem appears when the same request is made from browser JavaScript.&lt;br&gt;
The call succeeds, but the Metadata object in the AWS SDK response is empty.&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;// src/app/api/s3/_lib/s3Service.ts&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;getFileMetadata&lt;/span&gt; &lt;span class="o"&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;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="o"&gt;=&amp;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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HeadObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// {} without proper CORS&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// src/components/File.tsx&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setFilename&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;fileMetadata&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;getFileMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentId&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;...&lt;/span&gt;&lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// fileMetadata is empty, so this becomes undefined&lt;/span&gt;
        &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileMetadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;filename&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="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;setFilename&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="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request itself isn’t failing. S3 returns the metadata correctly.&lt;br&gt;
But because the code runs in the browser, certain response headers - including S3’s custom metadata headers - are not exposed to JavaScript by default.&lt;/p&gt;

&lt;p&gt;The next section explains why this happens.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why no Metadata on the Client
&lt;/h2&gt;

&lt;p&gt;Two fundamental security mechanisms are involved here: the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy" rel="noopener noreferrer"&gt;Same-Origin Policy (SOP)&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;Cross-Origin Resource Sharing (CORS)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;S3 CORS expose headers, custom metadata missing browser, or AWS CORS not working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Same-Origin Policy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Browsers enforce a rule that pages can only freely access resources from the same “origin,” defined by scheme, host, and port.&lt;br&gt;
Your frontend (&lt;code&gt;https://your-app.com&lt;/code&gt;) and S3 (&lt;code&gt;https://your-bucket.s3.amazonaws.com&lt;/code&gt;) are different origins, so &lt;em&gt;the browser restricts what your code can access&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. CORS as the exception&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CORS&lt;/em&gt; is a system that allows a server to relax these restrictions.&lt;br&gt;
If you configure S3 with an &lt;code&gt;AllowedOrigin&lt;/code&gt; matching your frontend, the browser is permitted to make the request.&lt;/p&gt;

&lt;p&gt;However, this only covers whether the request can happen - not what the browser exposes back to your JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Access-Control-Expose-Headers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even with &lt;em&gt;CORS&lt;/em&gt; enabled, the browser only exposes a small set of safe default headers to the client.&lt;br&gt;
S3’s custom metadata headers (x-amz-meta-*) are not part of that list.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;This means:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 sends the metadata correctly.&lt;/li&gt;
&lt;li&gt;The browser receives it.&lt;/li&gt;
&lt;li&gt;But the browser filters it out or hides it before the AWS SDK gets the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SDK sees no metadata headers and correctly (from its perspective) returns an empty Metadata object. Unless your S3 bucket explicitly exposes the metadata headers through CORS, your client code will always see Metadata: {}.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Fix It with S3 CORS Rules
&lt;/h2&gt;

&lt;p&gt;How do I fix S3 CORS so metadata shows up in HeadObject?&lt;/p&gt;

&lt;p&gt;To make your metadata visible in the browser, you need to add your metadata fields to &lt;code&gt;Access-Control-Expose-Headers&lt;/code&gt; in your S3 bucket’s CORS config so the browser is allowed to expose the headers you care about.&lt;/p&gt;

&lt;p&gt;Add them to its HEAD request responses. This is done by updating the CORS configuration on your S3 bucket, as detailed in the official &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/ManageCorsUsing.html" rel="noopener noreferrer"&gt;Amazon S3 User Guide on Managing CORS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A working XML example looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- S3 Bucket CORS Configuration (XML) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;CORSConfiguration&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;CORSRule&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AllowedOrigin&amp;gt;&lt;/span&gt;https://your-app.com&lt;span class="nt"&gt;&amp;lt;/AllowedOrigin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AllowedMethods&amp;gt;&lt;/span&gt;GET&lt;span class="nt"&gt;&amp;lt;/AllowedMethod&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AllowedMethods&amp;gt;&lt;/span&gt;HEAD&lt;span class="nt"&gt;&amp;lt;/AllowedMethod&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AllowedHeaders&amp;gt;&lt;/span&gt;*&lt;span class="nt"&gt;&amp;lt;/AllowedHeader&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ExposeHeaders&amp;gt;&lt;/span&gt;x-amz-meta-filename&lt;span class="nt"&gt;&amp;lt;/ExposeHeader&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ExposeHeaders&amp;gt;&lt;/span&gt;x-amz-meta-uploaded-by&lt;span class="nt"&gt;&amp;lt;/ExposeHeader&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Add an &amp;lt;ExposeHeader&amp;gt; for each custom metadata key you use --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/CORSRule&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CORSConfiguration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JSON Configuration Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;S&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bucket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;CORS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(JSON)&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;"CORSRules"&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="nl"&gt;"AllowedOrigins"&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="s2"&gt;"&amp;lt;https://your-app.com&amp;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;"AllowedMethods"&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="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HEAD"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"AllowedHeaders"&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="s2"&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;"ExposeHeaders"&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="s2"&gt;"x-amz-meta-filename"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x-amz-meta-uploadedat"&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;



&lt;p&gt;This enables client-side S3 HeadObject to return the full metadata object.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;A few important details:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expose only the headers you need.
The browser will ignore everything else.&lt;/li&gt;
&lt;li&gt;HEAD and GET both matter.
If you’re calling &lt;code&gt;HeadObjectCommand&lt;/code&gt; from the client, the HEAD method must be included or the request may fail depending on your setup.&lt;/li&gt;
&lt;li&gt;Wildcard &lt;code&gt;AllowedHeaders&lt;/code&gt; is fine here. It simply allows the browser to send custom headers, not expose them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this configuration is applied, the browser will no longer filter out the metadata, and your AWS SDK call will return the full Metadata object instead of {}.&lt;/p&gt;

&lt;p&gt;👉 Missed Part 1?&lt;br&gt;
Read the full upload/download pipeline here: &lt;a href="https://dev.to/vavilov2212/stop-uploading-files-to-s3-the-wrong-way-50le"&gt;Stop Uploading Files to S3 the Wrong Way&lt;/a&gt;&lt;/p&gt;

</description>
      <category>s3</category>
      <category>websecurity</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🚀 Stop Uploading Files to S3 the Wrong Way</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Tue, 14 Oct 2025 01:12:53 +0000</pubDate>
      <link>https://forem.com/vavilov2212/stop-uploading-files-to-s3-the-wrong-way-50le</link>
      <guid>https://forem.com/vavilov2212/stop-uploading-files-to-s3-the-wrong-way-50le</guid>
      <description>&lt;p&gt;When you let users upload files to S3, the most intuitive solution is to create an API route that accepts a &lt;em&gt;file&lt;/em&gt; and uses the &lt;em&gt;aws-sdk&lt;/em&gt; on the server to forward it to your bucket. This way each file transfer is done twice (2x) consuming resources and creating a bottleneck. &lt;br&gt;
Uploading files directly from the browser can save your server’s bandwidth and simplify infrastructure. Here’s how to do it securely and cleanly with react and Next.js.&lt;/p&gt;


&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Motivation&lt;/li&gt;
&lt;li&gt;Disposition&lt;/li&gt;
&lt;li&gt;The Core:  server operates entities&lt;/li&gt;
&lt;li&gt;Step 1: The Server Side API Route for Uploads&lt;/li&gt;
&lt;li&gt;
Step 2: A Decoupled Client Side Architecture

&lt;ul&gt;
&lt;li&gt;The Data Access Layer&lt;/li&gt;
&lt;li&gt;The Business Logic Layer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Step 3: Implementing Secure Downloads

&lt;ul&gt;
&lt;li&gt;A Deeper Look at the Client Side Download&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Handling Environment Variables&lt;/li&gt;
&lt;li&gt;Where to go from here?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;TL;DR&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you know what you’re doing and just need a working example reference, here you can find the &lt;a href="https://github.com/vavilov2212/s3-cloud-with-aws-sdk" rel="noopener noreferrer"&gt;complete code&lt;/a&gt; used in this article.&lt;br&gt;
This guide covers a secure file upload to S3 using react, next.js and AWS SDK v3 with pre-signed URLs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;The react next.js frontend app lets users perform a secure file upload to S3 directly from the browser and let them have access to those files afterwards. Pretty straightforward feature, but let’s get deeper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is an operations or support team that reviews those files in an internal admin board (e.g. verify contracts, evaluate numbers in reports etc.)&lt;/li&gt;
&lt;li&gt;These files contain sensitive information and can not be accessed publicly.&lt;/li&gt;
&lt;li&gt;The client user can only see his files and not other user’s files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of implementing your own static server solution, you can delegate storing and serverless file management to any S3-compatible object storage.&lt;/p&gt;
&lt;h2&gt;
  
  
  Disposition
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Whether you’re using Amazon S3, DigitalOcean Spaces, Linode Object Storage etc., this applies to any S3-compatible storage service. In order to upload and read files, we can use &lt;a href="https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3" rel="noopener noreferrer"&gt;@aws-sdk/client-s3&lt;/a&gt; for S3 compatible Object Storage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The most intuitive solution is often to create an API route that accepts a file and then uses the AWS-SDK on the server to forward it to your bucket. This server proxy makes your application the bottleneck.&lt;br&gt;
&lt;/p&gt;

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

  +------------------+       +---------------+       +---------------+
  │  Client Browser  │  →→→  │  Your Server  │  →→→  │   S3 Bucket   │
  +------------------+       +---------------+       +---------------+
         │                         │
   (1) Upload File           (2) Forward File
         │                         │
         ↖─────────────────────────↗
              File travels twice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every uploaded megabyte must be processed by &lt;em&gt;your server&lt;/em&gt;, consuming its bandwidth and memory, &lt;em&gt;before being sent on to S3&lt;/em&gt;. The file is transferred twice potentially exceeding memory or execution time limits (for large files).&lt;/p&gt;

&lt;p&gt;The same happens when a user downloads a file.&lt;/p&gt;

&lt;p&gt;The industry-standard solution is to offload this work by letting the client (browser) upload &lt;em&gt;directly&lt;/em&gt; to S3.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Core:  server operates entities&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of doing the heavy lifting, your api route performs a quick, secure handshake.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Client Requests an Upload:&lt;/strong&gt; Your front-end makes a small API call to your server, providing metadata like the file's content type (MIME type).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Server Generates a pre-signed URL:&lt;/strong&gt; Your server &lt;em&gt;which safely stores your s3 credential&lt;/em&gt; uses the SDK to generate a temporary, single-use URL with specific permissions (e.g., "allow a &lt;code&gt;PUT&lt;/code&gt; operation for &lt;code&gt;image/png&lt;/code&gt; for the next 10 minutes").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Client Uploads Directly to S3:&lt;/strong&gt; The client receives this URL and uses it to send the file straight to S3, bypassing your server entirely.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     +----------------------+
                     |      Your Server     |
                     | (Auth + Signed URLs) |
                     +----------+-----------+
                                ↕
                +---------------+---------------+
                |                               |
      (1) Request Upload URL           (1) Request Download URL
                |                               |
                ↓                               ↓
          +-----+-----+                   +-----+-----+
          |  Client   |                   |  Client   |
          | (Browser) |                   | (Browser) |
          +-----+-----+                   +-----+-----+
                |                               |
     (2) Upload File using URL       (2) Use URL to Download File
                |                               |
                ↓                               ↓
          +-----+-----+                   +-----+-----+
          |    S3     |   ←–––––––––––→   |    S3     |
          | (Storage) |  (3) File Stored  | (Storage) |
          +-----------+                   +-----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is secure, highly scalable, and more cost-effective. Let’s build this step by step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The following code examples are taken from a Next.js project that uses Feature-Sliced Design (FSD) for its architecture.&lt;br&gt;
You can follow along with the complete, working code in the example repository linked above.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: The Server Side API Route for Uploads&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This Next.js API route for S3 uploads, validates the request and generates the pre-signed URL. It's also the perfect place to enforce business logic, like attaching metadata.&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="c1"&gt;// src/app/api/upload/route.ts&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutObjectCommand&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="s2"&gt;@aws-sdk/client-s3&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;getSignedUrl&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="s2"&gt;@aws-sdk/s3-request-presigner&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;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="s2"&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="nx"&gt;crypto&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;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// --- This is a placeholder for your authentication logic ---&lt;/span&gt;
&lt;span class="c1"&gt;// In a real app, you would get the user's session from the request&lt;/span&gt;
&lt;span class="c1"&gt;// headers, cookies, or a token.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserIdFromSession&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;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Example: return the user ID from your auth system (e.g., NextAuth, Clerk)&lt;/span&gt;
  &lt;span class="c1"&gt;// For now, we'll return a hardcoded ID.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// -------------------------------------------------------------&lt;/span&gt;

&lt;span class="c1"&gt;// This is safe to initialize here as it only runs on the server.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_REGION&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&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;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;contentType&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;request&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;span class="c1"&gt;// const userId = 123; // In a real app, derive this from the user's session.&lt;/span&gt;
      &lt;span class="c1"&gt;// 1. Get the current user's ID securely on the server&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&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;getUserIdFromSession&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&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;command&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;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;uploadedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="na"&gt;uploadedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signedUrl&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;getSignedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// URL is valid for 10 minutes&lt;/span&gt;
      &lt;span class="k"&gt;return&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;signedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ...error handling&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;This API route is the foundation for secure serverless file uploads in any Next.js + AWS S3 SDK integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: A Decoupled Client Side Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To keep our code clean and testable, we'll separate frontend file upload logic from the business logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Data Access Layer&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This file's only job is to execute &lt;code&gt;fetch&lt;/code&gt; requests. It handles &lt;em&gt;how&lt;/em&gt; we communicate with our APIs.&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="c1"&gt;// src/shared/api/s3.ts&lt;/span&gt;

&lt;span class="c1"&gt;// Requests a pre-signed URL from our backend.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPresignedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signedUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&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="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="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;/api/upload&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="nx"&gt;contentType&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to get pre-signed URL.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Uploads the file to the provided S3 URL.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;uploadToS3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;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="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="nx"&gt;signedUrl&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;PUT&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;file&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="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;S3 upload failed.&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;h3&gt;
  
  
  &lt;strong&gt;The Business Logic Layer&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This file defines &lt;em&gt;what&lt;/em&gt; the feature does - the sequence of steps - without knowing the implementation details.&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="c1"&gt;// src/features/file-management/model/uploadFile.ts&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;getPresignedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uploadToS3&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="s2"&gt;@shared/api/s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This function orchestrates the upload process.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&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;try&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;signedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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="nf"&gt;getPresignedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&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;uploadToS3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 3: Upload file key to store in database&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log("Uploading file key to store in database...");&lt;/span&gt;
    &lt;span class="c1"&gt;// await storeUploadedFileKey(key, additionalData);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Upload failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="na"&gt;success&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="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;With this separation, your UI component only needs to import and call &lt;code&gt;uploadFile(theFile)&lt;/code&gt;. The component remains simple, and your logic is reusable and easy to unit test.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Implementing Secure Downloads&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The download flow uses the same secure pattern. First, we create an API route to generate a pre-signed &lt;code&gt;GetObject&lt;/code&gt; URL for a secure S3 file download.&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="c1"&gt;// src/app/api/download/route.ts&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetObjectCommand&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="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ... s3 client setup is the same ...&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&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;key&lt;/span&gt; &lt;span class="o"&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;nextUrl&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* handle error */&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;command&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;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// This header forces the browser to download the file.&lt;/span&gt;
        &lt;span class="na"&gt;ResponseContentDisposition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`attachment;`&lt;/span&gt;
        &lt;span class="c1"&gt;// You can override the filename for the user here&lt;/span&gt;
        &lt;span class="c1"&gt;// But then you must also know the file extension&lt;/span&gt;
        &lt;span class="c1"&gt;// ResponseContentDisposition: `attachment; filename="${key}"`&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;signedUrl&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;getSignedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// 5 minute validity&lt;/span&gt;
    &lt;span class="k"&gt;return&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;signedUrl&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;Now for the client-side, which involves a browser hack to trigger the download without navigating away from the page.&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="c1"&gt;// src/features/file-management/model/downloadFile.ts&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;getDownloadUrl&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="s2"&gt;@shared/api/s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Assumes a getDownloadUrl function in the shared API layer&lt;/span&gt;

&lt;span class="c1"&gt;// This function orchestrates the business logic for downloading a file.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;downloadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Step 1: Get the pre-signed download URL&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;signedUrl&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="nf"&gt;getDownloadUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 2: Trigger the download in the browser&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signedUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;download&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&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="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;File download process failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="na"&gt;success&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="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;Together, these steps form a scalable, production-ready file upload and download workflow using react Next.js, TypeScript, and AWS S3 SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A Deeper Look at the Client Side Download&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You might be wondering about the &lt;code&gt;link.click()&lt;/code&gt; part. Why not just use &lt;code&gt;window.location.href = signedUrl&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;First, a direct navigation would rip the user out of your single-page application. Second, for content types the browser can render (like images, PDFs, or text files), it would likely just display the file instead of saving it.&lt;/p&gt;

&lt;p&gt;The programmatic anchor tag (&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;) solves both problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;const link = document.createElement('a');&lt;/code&gt; We create a new, invisible link element in memory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;link.href = signedUrl;&lt;/code&gt; We set its destination to our secure, temporary S3 URL.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;link.setAttribute('download', key);&lt;/code&gt; This is the most important part. The &lt;code&gt;download&lt;/code&gt; attribute is a command to the browser: "Do not navigate to this &lt;code&gt;href&lt;/code&gt;. Instead, treat its content as a file to be saved, and use this attribute's value as the suggested filename." This forces the download behaviour.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;document.body.appendChild(link);&lt;/code&gt; The link must be attached to the document for it to be clickable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;link.click();&lt;/code&gt; We programmatically simulate a user clicking the link, which triggers the browser's "Save As..." dialog.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;document.body.removeChild(link);&lt;/code&gt; The link has done its job, so we immediately remove it from the DOM to keep our page clean.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Handling Environment Variables&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As a final note, manage your credentials using a &lt;code&gt;.env&lt;/code&gt; file and provide a template for other developers with a &lt;code&gt;.env.example&lt;/code&gt;. Be sure to add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env.example

# S3 Configuration

S3_BUCKET_NAME=your-s3-bucket-name
S3_REGION=your-bucket-region
S3_ENDPOINT=your-s3-endpoint
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Proper environment variable management for credentials is crucial for a secure S3 integration in any web app that uses serverless solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here?
&lt;/h2&gt;

&lt;p&gt;Once a file is in your S3 bucket, the next logical step is often to trigger serverless functions based on S3 events (e.g. S3 automation, Lambda triggers, and object storage workflows).&lt;/p&gt;

&lt;p&gt;You could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate thumbnails for uploaded images.&lt;/li&gt;
&lt;li&gt;Transcode files into different formats.&lt;/li&gt;
&lt;li&gt;Run data processing jobs on uploaded files.&lt;/li&gt;
&lt;li&gt;Notify other services that a new file is available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👇 Please share how you treat user-provided data?&lt;/p&gt;

&lt;p&gt;Do you analyse it with metrics? If so, what kinds of insights are you looking to uncover?&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>cloud</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Enforce Module Imports in FSD (using eslint-plugin-import)</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Mon, 22 Sep 2025 02:21:17 +0000</pubDate>
      <link>https://forem.com/vavilov2212/enforce-module-imports-in-fsd-using-eslint-plugin-import-2d72</link>
      <guid>https://forem.com/vavilov2212/enforce-module-imports-in-fsd-using-eslint-plugin-import-2d72</guid>
      <description>&lt;p&gt;Have you ever refactored a project and realised every file had a &lt;em&gt;different import style&lt;/em&gt;? This article dives deeper into managing code structure by enforcing rules with ESLint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Motivation&lt;/li&gt;
&lt;li&gt;Unforeseen Consequences&lt;/li&gt;
&lt;li&gt;Enforcing Code Structure&lt;/li&gt;
&lt;li&gt;Customising the Rule&lt;/li&gt;
&lt;li&gt;Future Exploration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you already know what you’re doing, check out the example repo: &lt;a href="https://github.com/vavilov2212/eslint-fsd-example" rel="noopener noreferrer"&gt;eslint-fsd-example&lt;/a&gt;. It follows the FSD (Feature Sliced Design) and serves as a practical showcase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;As a lead developer, one of my responsibilities is to ensure that the codebase stays coherent and manageable while complexity grows. When new features and fixes are introduced by a team of developers, it’s inevitable that their code looks different. That’s fine — each one brings a unique vision and perspective. Sometimes, the team even adopts those practices at scale.&lt;/p&gt;

&lt;p&gt;But coding style and structure are another story. Everyone has their own vision, and that’s great… until you want to refactor 😑.&lt;/p&gt;

&lt;p&gt;Imagine different parts of your code doing things their own way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&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="s2"&gt;shared/ui/Comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// baseUrl + direct file&lt;/span&gt;
&lt;span class="k"&gt;import&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="s2"&gt;../../ui/Comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// relative path&lt;/span&gt;
&lt;span class="k"&gt;import&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="s2"&gt;@/shared/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// alias + index file&lt;/span&gt;
&lt;span class="k"&gt;import&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="s2"&gt;../../ui/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// relative path to index&lt;/span&gt;
&lt;span class="c1"&gt;// and so on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens in real projects and leads to a &lt;strong&gt;zoo of approaches&lt;/strong&gt; scattered everywhere. Problems include circular imports, and broken refactors (auto-import often fails to replace paths correctly).&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;del&gt;Un&lt;/del&gt;foreseen Consequences
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://feature-sliced.design/docs/reference/public-api#circular-imports" rel="noopener noreferrer"&gt;Circular imports&lt;/a&gt; often appear when you import from an index file that re-exports the same module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/shared/ui/index.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&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;./Comment&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="o"&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;./Like&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="o"&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;./ViewsCounter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// src/shared/ui/Comment&lt;/span&gt;
&lt;span class="k"&gt;import&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;shared/ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- 🔥 causes a cycle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even without an index file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/shared/ui/Comment&lt;/span&gt;
&lt;span class="k"&gt;import&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;./Like&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// src/shared/ui/Like&lt;/span&gt;
&lt;span class="k"&gt;import&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;./ViewsCounter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// src/shared/ui/ViewsCounter&lt;/span&gt;
&lt;span class="k"&gt;import&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;./Comment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 This can even lead to &lt;strong&gt;runtime errors that are very hard to debug&lt;/strong&gt; (ask me how i know it).&lt;/p&gt;




&lt;h2&gt;
  
  
  Enforcing Code Structure
&lt;/h2&gt;

&lt;p&gt;FSD conventionally enforces strict import/export principles (&lt;a href="https://feature-sliced.design/docs/reference/public-api" rel="noopener noreferrer"&gt;they’re even described as mandatory in the docs&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any module may only be imported via its &lt;strong&gt;public API&lt;/strong&gt; (index file) using an &lt;strong&gt;absolute path&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Inside a module, all imports must use &lt;strong&gt;relative paths&lt;/strong&gt; (avoids cycles)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what stops developers from breaking this rule? Nothing 🤔 — unless you enforce it with ESLint.&lt;/p&gt;

&lt;p&gt;FSD provides a dedicated &lt;a href="https://github.com/feature-sliced/eslint-config" rel="noopener noreferrer"&gt;feature-sliced/eslint-config&lt;/a&gt; with just three rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;public-api&lt;/code&gt; – uses &lt;a href="https://github.com/import-js/eslint-plugin-import" rel="noopener noreferrer"&gt;eslint-plugin-import&lt;/a&gt;, adds glob expression to match patterns in your import statements (internally then converts glob expressions to regexp via &lt;a href="https://github.com/isaacs/minimatch" rel="noopener noreferrer"&gt;&lt;em&gt;minimatch&lt;/em&gt;&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;layers-slices&lt;/code&gt; – uses &lt;a href="https://github.com/javierbrea/eslint-plugin-boundaries" rel="noopener noreferrer"&gt;eslint-plugin-boundaries&lt;/a&gt;, analyses relations between the dependent element (your module) and the dependency (what’s being imported) and checks if it’s allowed or not (see &lt;a href="https://feature-sliced.design/docs/reference/layers#import-rule-on-layers" rel="noopener noreferrer"&gt;&lt;em&gt;fsd docs&lt;/em&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import-order&lt;/code&gt; – uses &lt;a href="https://github.com/import-js/eslint-plugin-import" rel="noopener noreferrer"&gt;eslint-plugin-import&lt;/a&gt;, sorts and groups imports alphabetically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a look at a &lt;a href="https://github.com/vavilov2212/eslint-fsd-example" rel="noopener noreferrer"&gt;Next.js example project&lt;/a&gt; that tries to follows FSD (Feature Sliced Design) methodology. Boilerplate comes from &lt;a href="https://github.com/vercel/next.js/tree/canary/packages/create-next-app" rel="noopener noreferrer"&gt;&lt;code&gt;create-next-app&lt;/code&gt;&lt;/a&gt; using the &lt;a href="https://github.com/vercel/next.js/tree/canary/examples/with-eslint" rel="noopener noreferrer"&gt;&lt;code&gt;with-eslint&lt;/code&gt;&lt;/a&gt; example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app &lt;span class="se"&gt;\&lt;/span&gt;
eslint-fsd-example &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# project name&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; https://github.com/vercel/next.js/tree/canary &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# example name or url&lt;/span&gt;
&lt;span class="nt"&gt;--example-path&lt;/span&gt; examples/with-eslint &lt;span class="c"&gt;# if example url has a branch name in it and then path to the example, it has to be specified separately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Customising the Rule
&lt;/h2&gt;

&lt;p&gt;Project structure (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&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;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;signin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&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;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;Signin&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;_pages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;signin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;Signin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;Signin&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;               &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;               &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;Signin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;                   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;Signin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;                   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;Signin&lt;/span&gt; &lt;span class="nx"&gt;actual&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;                   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;_features&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;sigin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;signinFormValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;           &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;useSigninForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_pages&lt;/code&gt; and &lt;code&gt;_features&lt;/code&gt; folders start with underscores to avoid naming conflicts with Next.js reserved folders. Other FSD layers use the same convention for consistency.&lt;/p&gt;

&lt;p&gt;We want to enforce imports only from &lt;strong&gt;public APIs&lt;/strong&gt; (index files). Here’s a simplified ESLint config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FS_LAYERS&lt;/span&gt; &lt;span class="o"&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;entities&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;features&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;widgets&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;app&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;processes&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;pages&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;shared&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;FS_SEGMENTS&lt;/span&gt; &lt;span class="o"&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;ui&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;types&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;model&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;lib&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;api&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;config&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;assets&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;nextjsConfigRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Linter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;import/no-internal-modules&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * Allow not segments import from slices
         * @example
         * 'entities/user/ui' // Pass
         * 'entities/user' // Also Pass
         */&lt;/span&gt;
        &lt;span class="s2"&gt;`**/*(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FS_SLICED_LAYERS_REG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)/!(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FS_SEGMENTS_REG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)/*(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FS_SEGMENTS_REG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="cm"&gt;/**
         * Allow slices with structure grouping
         * @example
         * 'features/auth/form' // Pass
         */&lt;/span&gt;
        &lt;span class="s2"&gt;`**/*(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FS_SLICED_LAYERS_REG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)/!(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FS_SEGMENTS_REG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)/!(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;FS_SEGMENTS_REG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="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;blockquote&gt;
&lt;p&gt;💡 For the &lt;code&gt;shared&lt;/code&gt; directory different rules apply since it’s somewhat special.&lt;br&gt;
Look in the source code for details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reaching past &lt;code&gt;src/_features/signin/lib/index.ts&lt;/code&gt; is prohibited — exactly what we want ✅.&lt;/p&gt;

&lt;p&gt;Here’s the proof:&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%2Fmwbb8wkfn9blazgf4nkm.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%2Fmwbb8wkfn9blazgf4nkm.png" alt="Proof that the rule works" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The FSD guidelines say you should only import from the slice root. Our team, however, prefers to allow imports from specific segments (like &lt;code&gt;ui&lt;/code&gt;, &lt;code&gt;lib&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;To achieve this, we had to adjust &lt;a href="https://github.com/import-js/eslint-plugin-import" rel="noopener noreferrer"&gt;eslint-plugin-import&lt;/a&gt; directly.&lt;/p&gt;

&lt;p&gt;If you want to follow the official FSD recommendations, you can just use the &lt;a href="https://github.com/feature-sliced/eslint-config" rel="noopener noreferrer"&gt;feature-sliced/eslint-config&lt;/a&gt; &lt;code&gt;public-api&lt;/code&gt; rule as is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Future Exploration
&lt;/h2&gt;

&lt;p&gt;While this setup covers the basics of enforcing FSD imports, there are still some &lt;strong&gt;open questions&lt;/strong&gt; worth exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://feature-sliced.design/docs/reference/public-api#public-api-for-cross-imports" rel="noopener noreferrer"&gt;&lt;strong&gt;Cross-imports&lt;/strong&gt;&lt;/a&gt; with &lt;code&gt;@x&lt;/code&gt; in FSD – Cross-imports are part of the official FSD methodology. They allow integration between slices or layers in a controlled way. But how do we enforce them properly with ESLint, and what are the best practices for keeping them consistent?&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs/app/guides/lazy-loading#nextdynamic" rel="noopener noreferrer"&gt;&lt;strong&gt;Dynamic imports&lt;/strong&gt;&lt;/a&gt; – Sometimes you need to use &lt;code&gt;import()&lt;/code&gt; directly for code-splitting. But what happens when you want to dynamically import something from a &lt;em&gt;public API&lt;/em&gt; instead of a deep file? Is that possible, or do you always end up importing the raw file?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing strategies&lt;/strong&gt; – Should tests respect the same public API import rules, or is it acceptable for them to reach into private modules?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t have all the answers yet (still experimenting 😅), but these are areas I’m actively digging into.&lt;/p&gt;

&lt;p&gt;👉 In a &lt;strong&gt;follow-up post&lt;/strong&gt;, I’ll share experiments and possible solutions for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making dynamic imports work with public APIs&lt;/li&gt;
&lt;li&gt;Handling test imports without breaking conventions&lt;/li&gt;
&lt;li&gt;Enforcing and scaling cross-imports without losing clarity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if that sounds interesting — stay tuned, because &lt;strong&gt;Part 2&lt;/strong&gt; is coming!&lt;/p&gt;




&lt;p&gt;👇 Share your experience in the comments — I’d love to hear how you handle these challenges!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>tooling</category>
      <category>frontend</category>
    </item>
    <item>
      <title>🧠 Master Your Prompt: A Practical Process to Improve AI Interactions</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Fri, 11 Jul 2025 00:36:29 +0000</pubDate>
      <link>https://forem.com/vavilov2212/master-your-prompt-a-practical-process-to-improve-ai-interactions-2jg3</link>
      <guid>https://forem.com/vavilov2212/master-your-prompt-a-practical-process-to-improve-ai-interactions-2jg3</guid>
      <description>&lt;h2&gt;
  
  
  Step-by-Step Guide to Analyze and Enhance Your Prompts:
&lt;/h2&gt;

&lt;p&gt;In today’s AI-driven world, the precision and structure of input prompts directly influence output quality, relevance, and consistency. Effective prompt engineering is a critical skill for developers, data scientists, and AI practitioners aiming to optimize model performance and reduce ambiguity. This article presents a practical, step-by-step process to analyze, refine, and enhance your prompts—helping you unlock the full potential of AI interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  📄 The Original Prompt
&lt;/h2&gt;

&lt;p&gt;The full prompt in text format → &lt;a href="https://gist.github.com/vavilov2212/7da0e77b682d108ec11478bb17dd752d" rel="noopener noreferrer"&gt;Prompt Refiner&lt;/a&gt; or click the image 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/vavilov2212/7da0e77b682d108ec11478bb17dd752d" rel="noopener noreferrer"&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%2Fzpgyte91rjwm972ztjsq.png" alt="Prompt Preview Image" width="800" height="1036"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎥 Source
&lt;/h2&gt;

&lt;p&gt;I stumbled upon this prompt structure on &lt;a href="https://www.facebook.com/matthewhitcham/videos/1182955480180921" rel="noopener noreferrer"&gt;Social media&lt;/a&gt; and titled as &lt;em&gt;"The World's best AI methods"&lt;/em&gt; by a content creator who sells prompt libraries.  &lt;/p&gt;




&lt;h2&gt;
  
  
  🌤️ Strong Design (for reference)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Clear, modular 2-phase structure&lt;/li&gt;
&lt;li&gt;Low cognitive load with simple diagnostics&lt;/li&gt;
&lt;li&gt;Deliverables are copy/paste-friendly&lt;/li&gt;
&lt;li&gt;Useful for tooling and collaborative workflows&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ‼️ Flaws and How to Enhance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. ⚡️ Assumes intent is always worth preserving&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ It prioritizes retaining original intent, even when that intent is vague or misguided.&lt;br&gt;
&lt;em&gt;But in real-world usage, input prompts are often vague, contradictory, or low-quality.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; “Make it cool and viral.” What does that mean? Tone? Format? Medium?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Add an early clarification step:&lt;br&gt;
“If the original intent is unclear, poorly defined, or counterproductive, rewrite the goal before proceeding.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. ⚡️ No fallback logic for poor or minimal inputs&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ You can't go forward if the prompt is unusable.&lt;br&gt;
&lt;em&gt;It should trigger structured fallback behavior, but there's none.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
    - Extremely short prompts (“Make it nice”)&lt;br&gt;
    - Prompts lacking context (“Write an article”)&lt;br&gt;
    - Unstructured lists without instruction&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Insert a &lt;strong&gt;Phase 0 – Precheck&lt;/strong&gt; step:&lt;br&gt;
“If input prompt lacks purpose, audience, or content type, request clarification before continuing.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. ⚡️ Doesn’t distinguish prompt types (creative vs. functional)&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ The same criteria are applied to all prompt types.&lt;br&gt;
&lt;em&gt;Prompts for storytelling or ideation shouldn’t be judged by the same standards as instructional prompts.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A story prompt might intentionally break clarity or tone norms to achieve voice, metaphor, or ambiguity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Add a &lt;strong&gt;Prompt Type Detection&lt;/strong&gt; step:&lt;br&gt;
“If prompt appears to be creative, conversational, or open-ended, adapt evaluation criteria. Skip irrelevant items like accuracy or resource usage.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. ⚡️ No resolution strategy for conflicting criteria&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ In practice, criteria can contradict each other:&lt;br&gt;
    • Improving &lt;strong&gt;clarity&lt;/strong&gt; may reduce &lt;strong&gt;brevity&lt;/strong&gt;&lt;br&gt;
    • Strengthening &lt;strong&gt;tone&lt;/strong&gt; may hurt &lt;strong&gt;resource efficiency&lt;/strong&gt;&lt;br&gt;
    • Increasing &lt;strong&gt;specificity&lt;/strong&gt; might restrict &lt;strong&gt;flexibility&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s no prioritization&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Define a default priority order: Clarity → Tone → Brevity → Efficiency&lt;br&gt;
“If two criteria conflict, prioritize the one that maximizes usefulness and comprehension.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🛠️ Enhanced Prompt
&lt;/h2&gt;

&lt;p&gt;The full prompt in text format → &lt;a href="https://gist.github.com/vavilov2212/230ef8b963bb5ed5a102b1d5c0966b08" rel="noopener noreferrer"&gt;Prompt Refiner Enhanced&lt;/a&gt; or click the image 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/vavilov2212/230ef8b963bb5ed5a102b1d5c0966b08" rel="noopener noreferrer"&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%2Fxo7m13asu65xc6yicr0d.png" alt="Prompt Preview Image" width="800" height="1092"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Would You Trust This?
&lt;/h2&gt;

&lt;p&gt;Community Insights Welcome!&lt;/p&gt;

&lt;p&gt;👉 Drop your thoughts below. I’ll reply to every comment.&lt;/p&gt;

</description>
      <category>promptengineering</category>
      <category>ai</category>
      <category>llm</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>From @import to @use: Migrating Sass the Right Way in a Next.js Project</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Wed, 02 Jul 2025 02:21:53 +0000</pubDate>
      <link>https://forem.com/vavilov2212/from-import-to-use-migrating-sass-the-right-way-in-a-nextjs-project-5h01</link>
      <guid>https://forem.com/vavilov2212/from-import-to-use-migrating-sass-the-right-way-in-a-nextjs-project-5h01</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;TL;DR: Sass Migration in a Nutshell&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Still using &lt;code&gt;@import&lt;/code&gt; in your SCSS? Time to level up. Here’s what I did:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Replaced&lt;/strong&gt; &lt;code&gt;@import&lt;/code&gt; with &lt;code&gt;@use&lt;/code&gt; 'styles/...' as *&lt;/p&gt;

&lt;p&gt;🛠️ Added &lt;strong&gt;resolving Sass Paths&lt;/strong&gt; for cleaner imports&lt;/p&gt;

&lt;p&gt;🧪 &lt;strong&gt;Verified&lt;/strong&gt; everything using npx sass-migrator — no issues found&lt;/p&gt;

&lt;p&gt;🧹 &lt;strong&gt;Reordered&lt;/strong&gt; CSS declarations above nested rules to remove deprecation warnings&lt;/p&gt;

&lt;p&gt;📁 &lt;strong&gt;Renamed&lt;/strong&gt; confusing css/ folder to styles/ for clarity&lt;/p&gt;

&lt;p&gt;🧘‍♂️ &lt;strong&gt;Result&lt;/strong&gt;: No more terminal spam, cleaner code, and a future-proof SCSS setup&lt;/p&gt;




&lt;p&gt;When working on a growing frontend project using &lt;strong&gt;Next.js&lt;/strong&gt; and &lt;strong&gt;Sass/SCSS&lt;/strong&gt;, it’s easy to miss the warnings piling up in the console — until they become impossible to ignore.&lt;/p&gt;

&lt;p&gt;Recently, I took on the task of cleaning up a Next.js codebase that was riddled with deprecated &lt;code&gt;@import&lt;/code&gt; usage and upcoming breaking changes related to how nested declarations behave. Here’s how I approached it, what I learned, and how you can apply it to your project.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🚨 Why Migrate from &lt;code&gt;@import&lt;/code&gt;?&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The &lt;code&gt;@import&lt;/code&gt; rule is deprecated and will be removed in future versions of Sass. Use &lt;code&gt;@use&lt;/code&gt; or &lt;code&gt;@forward&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sass-lang.com/documentation/breaking-changes/import/" rel="noopener noreferrer"&gt;sass-lang.com/documentation/breaking-changes/import/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may still see &lt;code&gt;@import&lt;/code&gt; sprinkled throughout your .scss files, and while it works &lt;em&gt;for now&lt;/em&gt;, it comes with hidden costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unpredictable global scoping&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No clear module boundaries&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duplicate style injection&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tons of warnings&lt;/strong&gt; in your terminal and browser console&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my case, the project had &lt;code&gt;@import&lt;/code&gt; '~css/colors' and similar statements all over .module.scss files. The tilde (~) was working magically, resolving to the src/ folder — although we never explicitly configured that (likely a leftover from legacy webpack behavior).&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🔁 The &lt;code&gt;@use&lt;/code&gt; Syntax&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The new module system introduced by Sass is smarter, safer, and designed to prevent namespace collisions. Here’s how I migrated:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Before&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'~css/colors'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'~css/vars'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'~css/mixins'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;✅ After&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'styles/colors'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'styles/vars'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'styles/mixins'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Along the way, I also renamed the folder from css/ to styles/ — it just makes more sense semantically and reads better for anyone joining the project.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧭 Resolving Sass Paths in Next.js&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To avoid long relative imports like ../../styles/colors, I initially configured the project like this:&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="c1"&gt;// next.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&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;path&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;sassOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;includePaths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allowed me to write clean import statements such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'styles/colors'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ It worked.&lt;/p&gt;

&lt;p&gt;⚠️ But there’s more you should know.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;❗includePaths Is a Legacy Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While includePaths is still supported by Dart Sass (and by the sassOptions field in Next.js), it’s not ideal for long-term use — especially with modern Sass modules.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 “The includePaths option is not recommended for use with &lt;code&gt;@use&lt;/code&gt;. Instead, prefer using relative or package-based URLs.”&lt;br&gt;
&lt;a href="https://sass-lang.com/documentation/at-rules/use/#url" rel="noopener noreferrer"&gt;sass-lang.com/documentation/at-rules/use/#url&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;🧩 Better Path Resolution Options&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅ 1. Use Relative Paths (Recommended)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of relying on global resolution, you can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'../../styles/colors'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a bit more verbose, but it’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit ✅&lt;/li&gt;
&lt;li&gt;Native to Sass ✅&lt;/li&gt;
&lt;li&gt;Portable across tools ✅&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;✅ 2. Use Webpack Aliases (Optional, with Caveats)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you want clean, short import paths &lt;strong&gt;and&lt;/strong&gt; you’re using Next.js with Webpack, you can configure an alias:&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="c1"&gt;// next.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&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;path&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@styles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/styles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&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;Then in SCSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'@styles/colors'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Sass doesn’t know what &lt;code&gt;@styles&lt;/code&gt; means unless your tooling resolves it (e.g. via sass-loader in Webpack).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;✅ 3. Use the SASS_PATH Environment Variable&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Define your base path globally using an environment variable.&lt;/p&gt;

&lt;p&gt;In a .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SASS_PATH=src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your SCSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'styles/colors'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works in &lt;strong&gt;Next.js&lt;/strong&gt; and many other Sass toolchains, and is a nice middle ground between global includes and explicit paths.&lt;/p&gt;

&lt;p&gt;✅ Built-in support&lt;/p&gt;

&lt;p&gt;✅ Cleaner imports&lt;/p&gt;

&lt;p&gt;⚠️ Slightly hidden magic&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;a href="https://github.com/sass/node-sass#environment-variables" rel="noopener noreferrer"&gt;node-sass on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;🛠 Using sass-migrator to Validate Migration&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Sass provides a handy CLI tool to help you migrate - &lt;a href="https://sass-lang.com/documentation/cli/migrator/" rel="noopener noreferrer"&gt;sass-migrator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s what I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx sass-migrator module &lt;span class="nt"&gt;--migrate-deps&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; ./src ./src/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.scss

&lt;span class="c"&gt;# stdout&lt;/span&gt;
Nothing to migrate!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which meant my manual changes were already valid under the module system — a good sanity check after a global find-and-replace in VSCode.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🔃 Fixing Declaration Order in Nested Rules&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Another warning that was spamming my console came from how some SCSS blocks had declarations &lt;em&gt;after&lt;/em&gt; nested selectors. Sass is aligning more with CSS behavior here.&lt;/p&gt;

&lt;p&gt;📚 More info here: &lt;a href="https://sass-lang.com/documentation/breaking-changes/mixed-decls/" rel="noopener noreferrer"&gt;https://sass-lang.com/documentation/breaking-changes/mixed-decls/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fix? Move all property declarations &lt;strong&gt;above&lt;/strong&gt; nested selectors. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;⚠️ Deprecated Style&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.fee&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;txt2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&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;
  
  
  &lt;strong&gt;✅ Fixed Style&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.fee&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;txt2&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;h2&gt;
  
  
  &lt;strong&gt;🧹 Final Thoughts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This was a small but meaningful refactor that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed console spam&lt;/li&gt;
&lt;li&gt;Future-proofed our stylesheets&lt;/li&gt;
&lt;li&gt;Improved module clarity&lt;/li&gt;
&lt;li&gt;Made onboarding easier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re using Sass in 2025, migrating to &lt;code&gt;@use&lt;/code&gt; isn’t just a nice-to-have If you’re maintaining a similar setup in Next.js. I highly recommend doing this migration sooner than later. It took me a couple of commits.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>sass</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🔀 macOS Split-Tunneling VPN Guide: Route Only Specific Subnets</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sat, 14 Jun 2025 09:15:56 +0000</pubDate>
      <link>https://forem.com/vavilov2212/routing-only-specific-subnets-through-vpn-split-tunneling-on-macos-2aig</link>
      <guid>https://forem.com/vavilov2212/routing-only-specific-subnets-through-vpn-split-tunneling-on-macos-2aig</guid>
      <description>&lt;p&gt;Many remote developers need to access company-internal services behind a VPN. The standard VPN setup? Connect using &lt;strong&gt;L2TP over IPsec&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But that usually &lt;strong&gt;routes &lt;em&gt;all&lt;/em&gt; your traffic through the VPN&lt;/strong&gt; — from websites to updates and personal apps.&lt;/p&gt;

&lt;p&gt;😬 Not ideal when you only need access to a specific internal subnet.&lt;/p&gt;

&lt;p&gt;Let’s fix that with this &lt;strong&gt;split tunneling&lt;/strong&gt; guide.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 What Is Split Tunneling?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Split tunneling&lt;/strong&gt; means routing &lt;em&gt;only&lt;/em&gt; specific IP ranges (like your company subnet) through the VPN, while everything else uses your normal internet connection.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet traffic  ─────────► stays local
Internal subnet   ─────────► goes through VPN

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

&lt;/div&gt;



&lt;p&gt;This results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ Better internet performance&lt;/li&gt;
&lt;li&gt;🔒 Better privacy&lt;/li&gt;
&lt;li&gt;🎯 More precise access to internal resources&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ❗ Default VPN Behavior on macOS
&lt;/h2&gt;

&lt;p&gt;If you're using &lt;strong&gt;System Settings &amp;gt; Network&lt;/strong&gt; to connect via L2TP over IPsec:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS &lt;strong&gt;routes all traffic&lt;/strong&gt; through the VPN once connected&lt;/li&gt;
&lt;li&gt;There’s &lt;strong&gt;no tun/ppp interface&lt;/strong&gt; visible in &lt;code&gt;ifconfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No native UI option for split tunneling&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔧 How To Manually Add a Route
&lt;/h2&gt;

&lt;p&gt;Let’s say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your &lt;strong&gt;internal company subnet&lt;/strong&gt; is &lt;code&gt;&amp;lt;YOUR_SUBNET&amp;gt;&lt;/code&gt; (e.g., &lt;code&gt;192.168.50.0/24&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;VPN gateway IP&lt;/strong&gt; is &lt;code&gt;&amp;lt;VPN_GATEWAY_IP&amp;gt;&lt;/code&gt; (e.g., &lt;code&gt;10.8.0.1&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After connecting to VPN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;route &lt;span class="nt"&gt;-n&lt;/span&gt; add &amp;lt;YOUR_SUBNET&amp;gt; &amp;lt;VPN_GATEWAY_IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔍 Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;route &lt;span class="nt"&gt;-n&lt;/span&gt; add 192.168.50.0/24 10.8.0.1
&lt;span class="c"&gt;# ✅ Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# add net 192.168.50.0: gateway 10.8.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚙️ Automating with &lt;code&gt;/etc/ppp/ip-up&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s automate route setup when PPP-based VPN connects.&lt;/p&gt;

&lt;p&gt;Create a new script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ppp/ip-up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# /etc/ppp/ip-up is triggered after a successful VPN connection&lt;/span&gt;
&lt;span class="c"&gt;# $5 is the VPN gateway IP passed by macOS&lt;/span&gt;

&lt;span class="nv"&gt;VPN_GATEWAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;VPN_GATEWAY_IP&amp;gt;"&lt;/span&gt;  &lt;span class="c"&gt;# e.g., 10.8.0.1&lt;/span&gt;
&lt;span class="nv"&gt;TARGET_SUBNET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR_SUBNET&amp;gt;"&lt;/span&gt;   &lt;span class="c"&gt;# e.g., 192.168.50.0/24&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;5&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VPN_GATEWAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    /sbin/route &lt;span class="nt"&gt;-n&lt;/span&gt; add &lt;span class="nt"&gt;-net&lt;/span&gt; &lt;span class="nv"&gt;$TARGET_SUBNET&lt;/span&gt; &lt;span class="nv"&gt;$5&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/ppp.log 2&amp;gt;&amp;amp;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /etc/ppp/ip-up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📄 The script logs output to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /tmp/ppp.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧼 Optional: Cleaning Up with &lt;code&gt;/etc/ppp/ip-down&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To remove the route automatically when VPN disconnects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ppp/ip-down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nv"&gt;VPN_GATEWAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;VPN_GATEWAY_IP&amp;gt;"&lt;/span&gt;  &lt;span class="c"&gt;# e.g., 10.8.0.1&lt;/span&gt;
&lt;span class="nv"&gt;TARGET_SUBNET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR_SUBNET&amp;gt;"&lt;/span&gt;   &lt;span class="c"&gt;# e.g., 192.168.50.0/24&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;5&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VPN_GATEWAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    /sbin/route &lt;span class="nt"&gt;-n&lt;/span&gt; delete &lt;span class="nt"&gt;-net&lt;/span&gt; &lt;span class="nv"&gt;$TARGET_SUBNET&lt;/span&gt; &lt;span class="nv"&gt;$5&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /etc/ppp/ip-down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📡 How to Test If the Route Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Check the Routing Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;netstat &lt;span class="nt"&gt;-nr&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &amp;lt;YOUR_SUBNET_PREFIX&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧪 Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;netstat &lt;span class="nt"&gt;-nr&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;192.168.50
&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# 192.168.50.0/24   10.8.0.1        UGSc           25       0     en0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Try a Ping
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping &amp;lt;INTERNAL_SERVER_IP&amp;gt;
&lt;span class="c"&gt;# Example:&lt;/span&gt;
&lt;span class="c"&gt;# ping 192.168.50.10&lt;/span&gt;
&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# 64 bytes from 192.168.50.10: icmp_seq=0 ttl=64 time=30.1 ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Read the Log File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/ppp.log
&lt;span class="c"&gt;# Example log:&lt;/span&gt;
&lt;span class="c"&gt;# add net 192.168.50.0: gateway 10.8.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 If there's an error (e.g., gateway not reachable), it will show up here.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 Visual: Full Tunnel vs. Split Tunnel
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔒 Full Tunnel (Default Behavior)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All traffic
    │
    ▼
[ VPN Gateway ] ─────► [ Company Resources ]
      ▲
      │
[ Your System ]──────► Internet (via VPN)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚡ Split Tunnel (Your Configuration)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Your System ]
   │       │
   │       └───────► Internet (direct)
   │
   └───────► VPN Gateway ───► &amp;lt;YOUR_SUBNET&amp;gt;
                         ▲
                         │
                 [ Company Resources ]

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  ❗ What If VPN Pushes a Default Route?
&lt;/h2&gt;

&lt;p&gt;Some VPN servers force your system to route &lt;em&gt;all&lt;/em&gt; traffic through them.&lt;/p&gt;

&lt;p&gt;To override:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Find Your Default Gateway
&lt;/h3&gt;

&lt;p&gt;Before connecting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;netstat &lt;span class="nt"&gt;-nr&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;default
&lt;span class="c"&gt;# Example output:&lt;/span&gt;
&lt;span class="c"&gt;# default            192.168.1.1        UGSc           86       0     en0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this value.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. After Connecting to VPN
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;route delete default
&lt;span class="nb"&gt;sudo &lt;/span&gt;route add default &amp;lt;YOUR_LOCAL_GATEWAY&amp;gt;
&lt;span class="c"&gt;# Example&lt;/span&gt;
&lt;span class="c"&gt;# sudo route delete default&lt;/span&gt;
&lt;span class="c"&gt;# sudo route add default 192.168.1.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Do this with caution — if you get it wrong, you may temporarily lose internet.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔌 L2TP/IPsec VPN on macOS routes all traffic by default&lt;/li&gt;
&lt;li&gt;🔀 Split tunneling lets you access only what's needed&lt;/li&gt;
&lt;li&gt;🛠️ Automate routes with &lt;code&gt;/etc/ppp/ip-up&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🧹 Clean up with &lt;code&gt;/etc/ppp/ip-down&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🧾 Logs are saved to &lt;code&gt;/tmp/ppp.log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🧪 Testing ensures everything’s working as expected&lt;/li&gt;
&lt;li&gt;🚧 Optionally override VPN-pushed routes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;man route&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man pppd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/networkextension" rel="noopener noreferrer"&gt;Apple VPN Developer Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Inject sass global variables from next.config.js - sass env variables</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Tue, 29 Apr 2025 13:53:40 +0000</pubDate>
      <link>https://forem.com/vavilov2212/inject-sass-global-variables-from-nextconfigjs-sass-env-variables-3nco</link>
      <guid>https://forem.com/vavilov2212/inject-sass-global-variables-from-nextconfigjs-sass-env-variables-3nco</guid>
      <description>&lt;h2&gt;
  
  
  Add global variable to sass compiler
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/vercel/next.js/tree/main/examples/with-next-sass" rel="noopener noreferrer"&gt;with-next-sass&lt;/a&gt; - This official next.js example demonstrates how to use Next.js' built-in Global Sass/Scss imports  and Component-Level Sass/Scss modules support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This &lt;a href="https://stackoverflow.com/a/61705701" rel="noopener noreferrer"&gt;stackoverflow answears&lt;/a&gt; helped me.&lt;/p&gt;

&lt;p&gt;Nextjs has built in sapport for sass. To use component isolated .sass or .scss files you need to install &lt;code&gt;sass&lt;/code&gt;.&lt;br&gt;
&lt;/p&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;sass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure sass compiler you can use &lt;code&gt;sassOptions&lt;/code&gt; in &lt;code&gt;next.config.js&lt;/code&gt;. &lt;a href="https://nextjs.org/docs/basic-features/built-in-css-support#customizing-sass-options" rel="noopener noreferrer"&gt;nextjs.org/#customizing-sass-options&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For instance, if you want to load assets from styles like &lt;code&gt;@font-face&lt;/code&gt; src and have different locations on prod and dev environments, you can do the following:&lt;/p&gt;

&lt;p&gt;In next config set &lt;code&gt;assetPrefix&lt;/code&gt; depending on the environment. Then, add &lt;code&gt;sassOptions&lt;/code&gt; variable.&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="c1"&gt;// next.config.js&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phase&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="c1"&gt;// when started in development mode `next dev` or `npm run dev`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;PHASE_DEVELOPMENT_SERVER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// when `next build` or `npm run build` is used&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isProd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;PHASE_PRODUCTION_BUILD&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;assetPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isProd&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/prod&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;/&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;sassOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;prependData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`$prefix: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;assetPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="c1"&gt;// Other configuration&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;...&lt;/span&gt;
    &lt;span class="nx"&gt;sassOptions&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;In .scss files now you can use this &lt;code&gt;prefix&lt;/code&gt; variable to prepend import paths.&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;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Open-Sans'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("#{$prefix}fonts/OpenSans-Regular.ttf")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>sass</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Sentry Dynamic Environment at Runtime on Client with Next.js</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sat, 14 Sep 2024 16:45:21 +0000</pubDate>
      <link>https://forem.com/vavilov2212/sentry-environment-on-client-in-nextjs-2opj</link>
      <guid>https://forem.com/vavilov2212/sentry-environment-on-client-in-nextjs-2opj</guid>
      <description>&lt;p&gt;This article explores how to configure the Sentry SDK with environment-specific information in a Next.js application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exposition
&lt;/h2&gt;

&lt;p&gt;Sentry understands and reads the following environment variables, so you don’t have to explicitly set them in your Sentry SDK init payload (refer to &lt;a href="https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#common-options" rel="noopener noreferrer"&gt;&lt;strong&gt;Sentry Docs - Common Config Options&lt;/strong&gt;&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SENTRY_DSN&lt;/li&gt;
&lt;li&gt;SENTRY_ENVIRONMENT&lt;/li&gt;
&lt;li&gt;SENTRY_RELEASE&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Challenge with .env variables:
&lt;/h2&gt;

&lt;p&gt;With Next.js, this only works for server side, because the framework DOES NOT expose .env variables on client for security reasons (refer to &lt;a href="https://nextjs.org/docs/app/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser" rel="noopener noreferrer"&gt;&lt;strong&gt;Next.js Docs - Bundling Environment Variables for the Browser&lt;/strong&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Sentry even states this in the &lt;a href="https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#environment" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;code&gt;environment&lt;/code&gt; section of the configuration docs&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to distinguish Sentry traces between staging environments on client we need to explicitly set the &lt;code&gt;environment&lt;/code&gt; key in the Sentry SDK init payload to a desired value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, NEXT_PUBLIC variables are hardcoded into JS bundle at build time&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sentry.client.config.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="cm"&gt;/*
   * The current environment of your application (e.g. "production").
   * Must be explicitly set, as SENTRY_ENVIRONMENT .env variable is not exposed on client
   */&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SENTRY_ENVIRONMENT&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;As we deploy different staging environments - we need to assign actual names of those environments to our Sentry SDK &lt;code&gt;environment&lt;/code&gt; key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible Solutions
&lt;/h2&gt;

&lt;p&gt;Currently there’s no way to natively achieve this in Next.js and people have been struggling with this (check out &lt;a href="https://github.com/getsentry/sentry-javascript/issues/5130" rel="noopener noreferrer"&gt;&lt;strong&gt;Github issue - Init with environment config not working&lt;/strong&gt;&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The official recommended way by Next.js is stated in their documentation &lt;a href="https://nextjs.org/docs/app/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser" rel="noopener noreferrer"&gt;&lt;strong&gt;Next.js docs - Bundling Environment Variables for the Browser&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you need access to runtime environment values, you'll have to setup your own API to provide them to the client (either on demand or during initialization).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, this approach doesn’t work for the Sentry Browser SDK as it's initiated during build time and runs before first components mount (as far as i understand).&lt;/p&gt;

&lt;p&gt;But there are multiple other ways to achieve this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have multiple builds for each environment - While technically possible, having separate builds for each environment is cumbersome and inefficient and is simply a bad practice (read &lt;a href="https://www.mikemcgarr.com/blog/build-once-deploy-many.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Build Once, Deploy Many&lt;/strong&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/getsentry/sentry-javascript/issues/9580#issuecomment-2140778308" rel="noopener noreferrer"&gt;&lt;strong&gt;Init Client Sentry SDK in Layout Component&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Point the &lt;code&gt;tunnel&lt;/code&gt; option of Sentry SDK to a proxy and set init values there (found in comments of the discussion from previous entry, but yet to be explored).&lt;/li&gt;
&lt;li&gt;Replace “inlined” &lt;code&gt;NEXT_PUBLIC&lt;/code&gt; variables values before stating the application - this is what i ended up utilising in commercial production&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommended Method (Workaround)
&lt;/h2&gt;

&lt;p&gt;The best practice so far is to actually run through JS bundle files right before starting the application and manually replacing values of &lt;code&gt;NEXT_PUBLIC&lt;/code&gt; variables “inlined” in the code like described in multiple sources already:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/itsrennyman/manage-nextpublic-environment-variables-at-runtime-with-docker-53dl"&gt;&lt;strong&gt;Manage NEXT_PUBLIC Environment Variables at Runtime with Docker&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://phase.dev/blog/nextjs-public-runtime-variables/" rel="noopener noreferrer"&gt;&lt;strong&gt;Updating public Next.js environment variables without rebuilds&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vercel/next.js/discussions/44628#discussioncomment-7632258" rel="noopener noreferrer"&gt;&lt;strong&gt;Before Starting the Server, We Change All NEXT_PUBLIC values&lt;/strong&gt;&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Set &lt;code&gt;NEXT_PUBLIC_SENTRY_ENVIRONMENT&lt;/code&gt; .env variable to any value you like as placeholder for future replacement. Make sure it’s unique, since that’s whats going to be searched and replaced.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env&lt;/span&gt;

&lt;span class="nv"&gt;NEXT_PUBLIC_SENTRY_ENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NEXT_PUBLIC_SENTRY_ENVIRONMENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Search and Replace
&lt;/h3&gt;

&lt;p&gt;Add shell script file to the root of your sources, it executes when running the container. It replaces the placeholder value you've set in previous step with the name of your actual staging environment before starting the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# replace_public_env_vars_and_start_server.sh&lt;/span&gt;

&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="sb"&gt;`&lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f  &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"s/NEXT_PUBLIC_SENTRY_ENVIRONMENT/&lt;/span&gt;&lt;span class="nv"&gt;$SENTRY_ENVIRONMENT&lt;/span&gt;&lt;span class="s2"&gt;/g"&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="c"&gt;# server.js is created by next build from the standalone output&lt;/span&gt;
&lt;span class="c"&gt;# https://nextjs.org/docs/pages/api-reference/next-config-js/output&lt;/span&gt;
&lt;span class="c"&gt;# CMD ["node", "server.js"]&lt;/span&gt;
node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be aware, that in order to successfully run, the script requires write permissions to files and the directory they are in - it must be executed under appropriate role on wherever you're running it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build and Run
&lt;/h3&gt;

&lt;p&gt;After building the image - simply point to shell script from the previous step.&lt;/p&gt;

&lt;p&gt;I use the &lt;a href="https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile" rel="noopener noreferrer"&gt;&lt;strong&gt;official Next.js "with-docker" example&lt;/strong&gt;&lt;/a&gt; file to build and run my applications - the following snippet is the bottom portion of this file with some changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile&lt;/span&gt;
...

# Make sure it can be executed
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"chmod"&lt;/span&gt;, &lt;span class="s2"&gt;"+x"&lt;/span&gt;, &lt;span class="s2"&gt;"/app/replace_public_env_vars_and_start_server.sh"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "/app/replace_public_env_vars_and_start_server.sh" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;In the browser sentry environment is set to different value on each staging server (in this instance - &lt;code&gt;tst&lt;/code&gt;)&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F85cs14a9ysfu85jq4p51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F85cs14a9ysfu85jq4p51.png" alt="Image description" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The "replace inlined variables" method offers a practical workaround for achieving environment differentiation in your application, but remember, that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This method involves additional build complexity.&lt;/li&gt;
&lt;li&gt;It's a workaround, not an officially supported solution by Next.js.&lt;/li&gt;
&lt;li&gt;It may not work if your deploy environments have limitations on executing scripts or assigning roles under which files are written and/or executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please, let me know in the comments if you're using some other solution to this or have any insight on how to make this one better! 😊 Thanks for reading!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Windows Installation won't work due to 'Unexpected 0x00005 Error' - How to Fix</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sun, 04 Aug 2024 13:25:05 +0000</pubDate>
      <link>https://forem.com/vavilov2212/windows-installation-wont-work-due-to-unexpected-0x00005-error-how-to-fix-406m</link>
      <guid>https://forem.com/vavilov2212/windows-installation-wont-work-due-to-unexpected-0x00005-error-how-to-fix-406m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to the problem
&lt;/h2&gt;

&lt;p&gt;When attempting to install windows 10/11 (I tried several distributions including official images) i encountered error “Unexpected 0x00005 error, verify source is available”. Possible causes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;faulty memory (storage or RAM)&lt;/li&gt;
&lt;li&gt;usb installation source filesystem - during installation some fíes may be created that are larger than 4gb - it’s better to use NTFS instead of FAT32&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There also might be indication that something’s off, when before the installation begins, selecting the unallocated space on the target storage drive triggers &lt;code&gt;Setup was unable to create a new system partition or locate an existing one&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If you’re flashing the USD drive from windows - make sure you run Rufus or any other program as Administrator - it’s absolutely necessary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my case, while installing the Tiny11 build of Windows, the installation failed at 33%. I figured this might be because some files generated during the process were larger than 4GB. On my Mac, I could only format the thumb drive to FAT32 since NTFS is not supported. I stumbled upon another solution: since the storage drive of the target machine is formatted with NTFS, we can boot from it using one of the partitions as the installation source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Following guide in &lt;a href="https://answers.microsoft.com/en-us/windows/forum/all/we-couldnt-create-a-new-partition-or-locate-an/acdb1dae-01d9-44f7-846c-433e5addcfc2" rel="noopener noreferrer"&gt;this Microsoft support unswear&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/o4ilMAAk1Q8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We shall be using &lt;code&gt;diskpart&lt;/code&gt; command line utility. When in boot screen of windows installer press fn+F10 to open the terminal. First format the disk, which you will use to install windows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diskpart
list disk
# list volume
# list partition
select disk 0 # from the output of the command above
clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there’s any sort of issues with cleaning the disk, we can check it for corrupt memory slots and fix them. Exit the diskpart by typing exit, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chkdsk /f /r
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way is to use System File Checker&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sfc /scannow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;em&gt;Side note&lt;/em&gt;&lt;br&gt;
There might be issues involved, that are out of the scope of this article, like it might be defective CPU or RAM, specific BIOS settings or just anything else really. While researching this issue there were many discussions that resolved in faulty hardware - so be aware.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we need to create partition on the target storage drive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diskpart
list disk
select disk 0 # from the output of the command above
create partition primary size=5000 # without the size argument it'll take all unallocated space
# 5gb is enough for the tiny11 build, official images require more space
format fs=ntfs quick # quick is really quicker
assign # to choose the letter must be followed by letter=H
active
list partition # check that partition exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 On newer machines the disk is required to have GPT style partition in order for the installation to start, but on older machines it has to be MBR and setting it active is crucial in this case. To clarify what your case might be you need to conduct your own research.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diskpart
select disk 0
convert GPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;Next, &lt;a href="https://stackoverflow.com/questions/7170683/copy-all-files-and-folders-from-one-drive-to-another-drive-using-dos-command-pr" rel="noopener noreferrer"&gt;copy your installation media contents&lt;/a&gt; to this new partition we’ve just created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xcopy c: d: /h /i /c /k /e /r /y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the machine and boot from storage device - unplug the USB - it’ll assure the installation screen actually comes from the internal drive.&lt;/p&gt;

&lt;p&gt;Following these steps should help resolve the "Unexpected 0x00005 error".&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Tips
&lt;/h2&gt;

&lt;p&gt;In BIOS, configure boot options:&lt;br&gt;
• Disable CSM and use legacy boot if needed.&lt;br&gt;
• For Tiny11 builds, disable TPM; for official Windows 11, enable TPM (since it requires it)&lt;br&gt;
• Consider disabling virtualization settings (e.g., Intel Virtualization).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please add your tips and solutions in the comments to help others who are struggling to resolve these installation errors!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tiny11</category>
      <category>windows</category>
      <category>partition</category>
      <category>errors</category>
    </item>
    <item>
      <title>SVGs in React (almost everything you need to know)</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sun, 21 Apr 2024 04:16:06 +0000</pubDate>
      <link>https://forem.com/vavilov2212/svgs-in-react-almost-everything-you-need-to-know-1nb4</link>
      <guid>https://forem.com/vavilov2212/svgs-in-react-almost-everything-you-need-to-know-1nb4</guid>
      <description>&lt;p&gt;SVG stands for &lt;strong&gt;Scalable Vector Graphic&lt;/strong&gt;. True to its name, SVGs are infinitely scalable because they use XML syntax. This means they're essentially a set of instructions for the browser (or any program that can interpret them) to render the image. Unlike raster images (like JPEGs), SVGs don't have a fixed number of pixels, so they can be scaled to any size without losing quality.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;In the pre-JSON era, XML (Extensible Markup Language)&lt;/strong&gt; was the primary way to store and transmit data on the web. It provides a set of rules for encoding and reconstructing various data formats. Interestingly, both HTML and SVG use XML syntax with tags and attributes. You can even style SVG elements with CSS just like you would style regular HTML elements!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Real-world Scenario: Rendering SVGs in React&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's explore different ways to render SVGs in your React projects.&lt;/p&gt;

&lt;p&gt;💡 Code mentioned in this article, can be found in this repository&lt;br&gt;
&lt;a href="https://github.com/vavilov2212/render-svg-example"&gt;GitHub - vavilov2212/render-svg-example&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Rendering SVGs as Image Tags&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One approach is to treat SVGs as external resources. This way, you don't need any additional configuration, and you still benefit from SVG's scalability. Simply import the path to your SVG file and use it as the &lt;code&gt;src&lt;/code&gt; attribute of an &lt;code&gt;img&lt;/code&gt; tag (the file must be publicly accessible).&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="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&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="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;import&lt;/span&gt; &lt;span class="nx"&gt;Logo&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;../public/logo.svg&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&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;&lt;/span&gt;&lt;span class="nt"&gt;div&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="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; Hello, World! &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo.svg"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"svg"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"My SVG Image"&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffg74a5vxa19xyeup8rpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffg74a5vxa19xyeup8rpf.png" alt="Rendering SVGs as Image Tags" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; In this approach, only attributes applicable to the &lt;code&gt;img&lt;/code&gt; tag itself will have any effect. You won't be able to directly style the underlying SVG elements within the image.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Rendering SVGs "as is" in React&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Imagine you want your React components to be fully built and ready to render at initial page load, without waiting for external images to download. While simply including the SVG code within your component's markup might seem like an option, it's inefficient. We want to store static resources like SVGs separately from our JavaScript code.&lt;/p&gt;

&lt;p&gt;However, there's a catch. When you try to import an SVG file into your component, JavaScript module resolution expects valid JavaScript code, but SVGs are written in XML. Here's where build tools like Webpack come in to help!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gsabzjwvz8090qilrs8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gsabzjwvz8090qilrs8.png" alt="Import error" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 For deeper insights of what’s going to happen further, i recommend watching this 10 min video &lt;a href="https://www.youtube.com/watch?v=HZcXHNrkZKw"&gt;Tech Talk: RegEx (SVG) &amp;amp; JSX: Adapting Vector Graphics for React&lt;/a&gt;. In my dev practice, i’ve done exactly this a couple of times before switching to webpack automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Webpack to the rescue!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Webpack can be configured to preprocess SVGs during the build process, transforming them into React components that you can easily use within your application. To achieve this, we'll leverage a package called &lt;code&gt;@svgr/webpack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s edit webpack.config file to automatically transform svg into react component during build time. Install &lt;a href="https://github.com/gregberge/svgr"&gt;@svgr/webpack&lt;/a&gt; and follow &lt;a href="https://react-svgr.com/docs/webpack/#usage"&gt;usage guide&lt;/a&gt;.&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="c1"&gt;// webpack.config.js&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;rules&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;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;svg$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;jt&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;sx&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resourceQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/url/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// exclude react component if *.svg?url&lt;/span&gt;
      &lt;span class="na"&gt;use&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="s2"&gt;@svgr/webpack&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test: /\.svg$/i&lt;/code&gt;: This line tells Webpack to identify files ending with the &lt;code&gt;.svg&lt;/code&gt; extension (case-insensitive).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;issuer: /\.[jt]sx?$/&lt;/code&gt;: This line ensures the rule applies only when the SVG is imported within a JavaScript or TypeScript file (&lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.jsx&lt;/code&gt;, &lt;code&gt;.ts&lt;/code&gt;, or &lt;code&gt;.tsx&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resourceQuery: { not: [/url/] }&lt;/code&gt;: This line excludes SVGs used with the &lt;code&gt;url()&lt;/code&gt; loader (used for embedding small SVGs directly in CSS).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use: ["@svgr/webpack"]&lt;/code&gt;: This line specifies the &lt;code&gt;@svgr/webpack&lt;/code&gt; loader to handle SVG processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Using SVG Components:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once Webpack processes your SVGs, you can import and use them as React components:&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="c1"&gt;// src/App.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Logo&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;path/to/logo.svg&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path/to/styles.css&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&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;Logo&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svg&lt;/span&gt;&lt;span class="dl"&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Styling SVG Components:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By transforming SVGs into React components using SVGR, you gain the ability to style the underlying SVG elements directly within your CSS.&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="c1"&gt;// styles.css&lt;/span&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="nx"&gt;ellipse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;This CSS code will target all &lt;code&gt;ellipse&lt;/code&gt; elements within the &lt;code&gt;Logo&lt;/code&gt; component you imported, making their stroke red.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sfu3lfqrwy8pc30397s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sfu3lfqrwy8pc30397s.png" alt="Styling SVG Components" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, SVGR processed our import making it possible to simple call it as function and pass props to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  SVG optimisation with SVGR (utilising SVGO)
&lt;/h2&gt;

&lt;p&gt;SVGR package uses &lt;a href="https://github.com/svg/svgo/"&gt;SVGO&lt;/a&gt; to optimise SVG code before transforming it into react component. Utilising this may help you to dramatically reduce bundle size.&lt;/p&gt;

&lt;p&gt;Refer to the &lt;a href="https://react-svgr.com/docs/options/#svgo"&gt;configuration docs&lt;/a&gt; for available options or use both packages separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  SVGs as Data URLs
&lt;/h2&gt;

&lt;p&gt;This technique allows to store SVGs as strings using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs"&gt;Data URL scheme&lt;/a&gt;, for example:&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;some&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data:image/svg+xml,&amp;lt;svg-string&amp;gt;&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;Refer to this helpful article &lt;a href="https://medium.com/@ians/rendering-svgs-as-images-directly-in-react-a26615c45770"&gt;Rendering SVGs as images directly in React&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Advanced Topics. SVG Sprites&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SVG sprites are a technique for combining multiple SVGs into a single image file. This can improve performance by reducing the number of HTTP requests needed to render multiple SVGs on a page. However, managing complex SVG sprites can become cumbersome.&lt;/p&gt;

&lt;p&gt;Refer to this helpful article: &lt;a href="https://medium.com/@hayavuk/complete-guide-to-svg-sprites-7e202e215d34"&gt;Complete guide to SVG sprites&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With webpack we can use &lt;a href="https://github.com/JetBrains/svg-sprite-loader"&gt;svg-sprite-loader&lt;/a&gt; and it can be used in conjunction with other loaders like so:&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="c1"&gt;// webpack.config.js v.4&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nl"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rules&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;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sr"&gt;.svg$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;`svg-sprite-loader?&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[name].[hash]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;prefixize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;`svgo-loader?&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;plugins&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;removeTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;convertPathData&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;removeUselessStrokeAndFill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="s2"&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="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;
  
  
  &lt;strong&gt;Further Resources:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/jakearchibald/svgomg"&gt;SVGOMG&lt;/a&gt; - Online tool for optimizing SVGs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/topics/svg-path"&gt;SVG Path Visualizer&lt;/a&gt; - Tool to visualize and understand SVG paths&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;By understanding the different approaches to using SVGs in React and leveraging tools like SVGR and Webpack, you can effectively integrate scalable vector graphics into your React applications. This guide has equipped you with the knowledge to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Render SVGs as images or React components&lt;/li&gt;
&lt;li&gt;Style SVG elements directly within your components&lt;/li&gt;
&lt;li&gt;Optimize SVGs for better performance&lt;/li&gt;
&lt;li&gt;Consider best practices and explore advanced techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this guide empowers you to create visually appealing and performant React applications using SVGs!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>WebSockets: Connect and Interact in terminal</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Thu, 11 Jan 2024 04:19:23 +0000</pubDate>
      <link>https://forem.com/vavilov2212/websockets-connect-and-interact-in-real-time-jd1</link>
      <guid>https://forem.com/vavilov2212/websockets-connect-and-interact-in-real-time-jd1</guid>
      <description>&lt;h2&gt;
  
  
  Additional Resources:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket RFC: &lt;a href="https://www.rfc-editor.org/rfc/rfc6455.html"&gt;https://www.rfc-editor.org/rfc/rfc6455.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;cURL documentation: &lt;a href="https://curl.se/docs/manpage.html"&gt;https://curl.se/docs/manpage.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebSockets provide a way to establish two-way, real-time communication between client and a server. Compared to traditional HTTP requests, WebSockets offer continuous data exchange without the need for repeated connections, which means through a single TCP/IP socket connection. This makes them ideal for applications like live chat, streaming platforms, and real-time games.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf0we3cseadc6ofoucep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf0we3cseadc6ofoucep.png" alt="WebSocket protocol illustration" width="405" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, &lt;u&gt;not all tools directly support WebSockets out of the box&lt;/u&gt;. This guide will show you how to leverage cURL and explore alternative options to establish and interact with WebSocket connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Using cURL&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While cURL doesn't natively support WebSockets, you can mimic the handshake process by manually specifying websocket specific headers and flags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Indicates a persistent connection with upgrade to WebSocket protocol&lt;/span&gt;
Connection: keep-alive, upgrade
Upgrade: websocket
&lt;span class="c"&gt;# Random base64 encoded key&lt;/span&gt;
Sec-WebSocket-Key: &amp;lt;your_websocket_key&amp;gt;
Sec-WebSocket-Version: 13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 For cUrl not to terminate its process when there’s no stdout you should use &lt;code&gt;--no-buffer&lt;/code&gt; or &lt;code&gt;-N&lt;/code&gt; flag (for short).&lt;br&gt;
From &lt;a href="https://curl.se/docs/manpage.html#-N"&gt;cUrl docs:&lt;/a&gt;&lt;br&gt;
&lt;em&gt;In normal work situations, curl uses a standard buffered output stream that has the effect that it outputs the data in chunks, not necessarily exactly when the data arrives. Using this option disables that buffering.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following command connects to &lt;/p&gt;

&lt;p&gt;&lt;code&gt;wss://ws.garantex.org/?stream=global&amp;amp;stream=btcrub&amp;amp;stream=ext_markets&amp;amp;stream=order&amp;amp;stream=trade&amp;amp;stream=member_balance&amp;amp;stream=exchanger&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and subscribes to various scenarios, defined on the server.&lt;/p&gt;

&lt;p&gt;Notice how it specifically uses http1.1, because http2 works in a different way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--no-buffer&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-sSv&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--http1&lt;/span&gt;.1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Connection: keep-alive, upgrade"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Upgrade: websocket"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Sec-WebSocket-Key: N509hQQwSFGfanhbxP3F6g=="&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Sec-WebSocket-Version: 13"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=global"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=btcrub"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=ext_markets"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=order"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=trade"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=member_balance"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--url-query&lt;/span&gt; &lt;span class="s2"&gt;"stream=exchanger"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
https://ws.garantex.org/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note:&lt;br&gt;
&lt;code&gt;-s or —silent&lt;/code&gt; to suppress progress meter&lt;br&gt;
&lt;code&gt;-S or —show-error&lt;/code&gt; in addition to -s disables progress meter but still show error messages&lt;br&gt;
&lt;code&gt;-v or --verbose&lt;/code&gt; to show request and response headers&lt;br&gt;
You may use just a single query sting &lt;code&gt;--url-query "stream=global&amp;amp;stream=btcrub&amp;amp;stream=ext_markets&amp;amp;stream=order&amp;amp;stream=trade&amp;amp;stream=member_balance&amp;amp;stream=exchanger”&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Beyond cURL: Alternative WebSocket Clients&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There are not many (that i could find)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cUrl kind of supports it, but i have not tested if you can communicate to server via stdin&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://httpie.io/docs/cli/examples"&gt;httpie&lt;/a&gt; sadly does &lt;u&gt;not&lt;/u&gt; support websocket protocol (both cli and gui app)&lt;/li&gt;
&lt;li&gt;postman does support websockets, but it’s paid software now&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://insomnia.rest/"&gt;insomnia&lt;/a&gt; is pretty good (but personally i like httpie more) and does support websockets (free for personal use)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  There are dedicated cli utilities to working with websockets:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/websockets/wscat"&gt;wscat&lt;/a&gt; is nodejs based (so you will actually need to set up node environment)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wscat &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"wss://ws.garantex.org/?stream=global"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/vi/websocat"&gt;websocat&lt;/a&gt; native command-line tool, but i found it a bit overly complicated for my sort of task&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;websocat &lt;span class="s2"&gt;"wss://ws.garantex.org/?stream=global"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Others to explore
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/erebe/wstunnel"&gt;wstunnel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joewalnes/websocketd"&gt;websocketd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alexanderGugel/wsd"&gt;wsd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Browser (any modern browser)
&lt;/h2&gt;

&lt;p&gt;In ECMAScript 6 (ES6) &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket"&gt;WebSocket API&lt;/a&gt; was added to javascript, so you may simply open devtools console and type in:&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="c1"&gt;// creates connection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&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;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://ws.garantex.org/?stream=global&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// returns current state of the connection&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt;

&lt;span class="c1"&gt;// prints out response messages&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &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="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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// communicate with the server&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&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 world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// self explanatory&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Masked input &amp; text</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sun, 10 Dec 2023 02:33:51 +0000</pubDate>
      <link>https://forem.com/vavilov2212/masked-input-text-142n</link>
      <guid>https://forem.com/vavilov2212/masked-input-text-142n</guid>
      <description>&lt;p&gt;There are a bunch of options of how to achieve masked inputs and text representation in general.&lt;/p&gt;

&lt;p&gt;I’m going to focus on react implementations here.&lt;/p&gt;




&lt;h2&gt;
  
  
  react-masked-input
&lt;/h2&gt;

&lt;p&gt;The one, that i used lately is &lt;a href="https://github.com/lucasbasquerotto/react-masked-input" rel="noopener noreferrer"&gt;react-masked-input&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the killer features if, that this library exposes simple function, to mask not just input, but any string or value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createDynamicNumberMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createDefaultMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;,&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;react-hook-mask&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="nf"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;myValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;createDefaultMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+99 (999) 999 99 99&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="c1"&gt;// or with dynamic mask&lt;/span&gt;

&lt;span class="nf"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;myValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;createDynamicNumberMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+9 (999) 999 99 99&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;+99 (999) 999 99 99&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;+999 (999) 999 99 99&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 result is:&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%2Fbek9buwjtpwgc3grku5y.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%2Fbek9buwjtpwgc3grku5y.png" alt="Masked phone number representation" width="534" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add customisation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;,&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;react-hook-mask&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;MY_RULES&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;Map&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;C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&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;N&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&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;const&lt;/span&gt; &lt;span class="nx"&gt;createMaskGenerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mask&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="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MY_RULES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generateMask&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &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;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C&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;/&lt;/span&gt;&lt;span class="se"&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;return&lt;/span&gt; &lt;span class="nx"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// and you can conveniently add transformations&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;span class="nf"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;createMaskGenerator&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has even more, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;getting expected cursor position,&lt;/li&gt;
&lt;li&gt;unmask,&lt;/li&gt;
&lt;li&gt;4 types of hook for any needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pretty neat, but underrated, only 22 stars on github.&lt;/p&gt;




&lt;h2&gt;
  
  
  react-rxinput
&lt;/h2&gt;

&lt;p&gt;💡 It has a &lt;a href="https://nurulc.github.io/rxinput.html" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; page&lt;/p&gt;

&lt;p&gt;The other one, that’s interesting is &lt;a href="https://github.com/nurulc/react-rxinput" rel="noopener noreferrer"&gt;react-rxinput&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;It operates on the same principle of handling mask pattern as regular expression (which is cool). As to working with input element it uses &lt;code&gt;RXInputMask&lt;/code&gt; which does all the heavy lifting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RXInputMask&lt;/code&gt; comes from &lt;a href="https://github.com/nurulc/incr-regex-package" rel="noopener noreferrer"&gt;incr-regex-package&lt;/a&gt; - a module to handle regular expression validation in &lt;em&gt;steps&lt;/em&gt; instead of &lt;em&gt;all at once.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I haven't utilised it in any of my projects due to lack of ongoing development, but it's nice to experiment with the concept and draw inspiration from it.&lt;/p&gt;




&lt;h2&gt;
  
  
  react-phone-input-2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/bl00mber/react-phone-input-2" rel="noopener noreferrer"&gt;react-phone-input-2&lt;/a&gt; has more of a narrowly focused approach. But i did use it at my job on a project that needed &lt;em&gt;just this functionality&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It’s basically 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%2Fkpykn9p6gqy2x1f4axph.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%2Fkpykn9p6gqy2x1f4axph.png" alt="Demo of rendered inputs" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 The set of countries is predefined as array in a separate file, and was last updated in 2021.&lt;/p&gt;

&lt;p&gt;The library gives you an opportunity to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;filter out only countries, that you actually  need,&lt;/li&gt;
&lt;li&gt;apply custom sorting,&lt;/li&gt;
&lt;li&gt;add localisation (on top of predefined ones),&lt;/li&gt;
&lt;li&gt;add custom masks,&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Others
&lt;/h2&gt;

&lt;p&gt;There are other packages, which have more stars, like &lt;a href="https://github.com/sanniassin/react-input-mask" rel="noopener noreferrer"&gt;react-input-mask&lt;/a&gt; or &lt;a href="https://github.com/insin/react-maskedinput" rel="noopener noreferrer"&gt;react-maskedinput&lt;/a&gt;. They are both great libraries, which achieve what they aim for.&lt;/p&gt;

&lt;p&gt;That’s it, thanks for reading!&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>inputmask</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
