<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: vavilov2212</title>
    <description>The latest articles on DEV Community by vavilov2212 (@vavilov2212).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F927865%2F853c5ee9-29cb-4120-8e38-817c86623f59.jpg</url>
      <title>DEV Community: vavilov2212</title>
      <link>https://dev.to/vavilov2212</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vavilov2212"/>
    <language>en</language>
    <item>
      <title>Run -&gt; Log -&gt; Distill: a self-improving memory for AI-assisted development</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sun, 12 Jul 2026 16:38:08 +0000</pubDate>
      <link>https://dev.to/vavilov2212/run-log-distill-a-self-improving-memory-for-ai-assisted-development-bgo</link>
      <guid>https://dev.to/vavilov2212/run-log-distill-a-self-improving-memory-for-ai-assisted-development-bgo</guid>
      <description>&lt;p&gt;Every AI coding session starts with amnesia.&lt;/p&gt;

&lt;p&gt;Yesterday the agent spent two hours hunting down a state-sharing bug. Today it will happily reintroduce that same bug, in the same codebase, with the same confidence. The context window closed, and everything the agent learned went with it.&lt;/p&gt;

&lt;p&gt;I'm building a full-stack GraphQL store (Next.js + Keystone 6 + PostgreSQL, dissected in the rest of this series) almost entirely through AI-assisted sessions in Claude Code. A few sessions in, I got tired of the amnesia and changed the workflow itself. I now run every task with &lt;code&gt;Run → Log → Distill&lt;/code&gt;, built from these ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;while the agent works, it appends to a &lt;strong&gt;WAL&lt;/strong&gt; (a write-ahead log: the work journal);&lt;/li&gt;
&lt;li&gt;when the work is done, a &lt;strong&gt;compaction&lt;/strong&gt; step melts that log into a &lt;strong&gt;post-mortem&lt;/strong&gt;: a small file of rules that every future session must read before writing code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is being reinvented all over the industry right now. The tools are shipping it as a native feature: &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;Claude Code auto memory&lt;/a&gt;, Cursor Memories, &lt;a href="https://docs.devin.ai/desktop/cascade/memories" rel="noopener noreferrer"&gt;Devin's Cascade Memories&lt;/a&gt;. Agents that write reflections on their own failures and reread them on the next attempt are described here: &lt;a href="https://arxiv.org/abs/2303.11366" rel="noopener noreferrer"&gt;Reflexion&lt;/a&gt; or here &lt;a href="https://every.to/guides/compound-engineering" rel="noopener noreferrer"&gt;compounding engineering&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The pattern described in this article moves with you between tools, works today in any of them, and nothing enters long-term memory without your review. More links in prior art at the end.&lt;/p&gt;

&lt;p&gt;Below are three prompts you can copy as-is.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Chat history is not knowledge&lt;/li&gt;
&lt;li&gt;Two kinds of memory: a WAL and a post-mortem&lt;/li&gt;
&lt;li&gt;The lifecycle at a glance&lt;/li&gt;
&lt;li&gt;
Case 1: a planned refactor

&lt;ul&gt;
&lt;li&gt;Prompt 1: execute the plan&lt;/li&gt;
&lt;li&gt;Prompt 2: compact the journal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Case 2: a spontaneous session

&lt;ul&gt;
&lt;li&gt;Prompt 3: distill a spontaneous session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The loop closes&lt;/li&gt;
&lt;li&gt;Don't keep the journal in CLAUDE.md&lt;/li&gt;
&lt;li&gt;Limitations&lt;/li&gt;
&lt;li&gt;Prior art&lt;/li&gt;
&lt;li&gt;Steal it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Chat history is not knowledge
&lt;/h2&gt;

&lt;p&gt;Most AI coding setups already have some persistent context. Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt;, Cursor has rules files. In practice these hold static facts: how to run the dev server, where the tests live, what the folders mean.&lt;/p&gt;

&lt;p&gt;The knowledge that actually matters looks different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;why&lt;/em&gt; a refactor was done, so the next session doesn't undo it;&lt;/li&gt;
&lt;li&gt;which innocent-looking pattern caused a production bug, and the exact command that diagnoses it;&lt;/li&gt;
&lt;li&gt;which corners were cut on purpose, so they get revisited instead of rediscovered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the agent write transcripts about its own work, while the context is still in the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of memory: a WAL and a post-mortem
&lt;/h2&gt;

&lt;p&gt;Cognitive science splits memory into &lt;strong&gt;episodic&lt;/strong&gt; (what happened to me: "I burned my hand on that kettle") and &lt;strong&gt;semantic&lt;/strong&gt; (what I know: "kettles are hot"). A brain constantly converts the first into the second. &lt;a href="https://arxiv.org/abs/2309.02427" rel="noopener noreferrer"&gt;CoALA - Cognitive Architectures for Language Agents&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The WAL.&lt;/strong&gt; Before a database touches its real data structures, it appends the intended change to a write-ahead log. Appending to the end of a file is the fastest, safest thing a disk can do. If the process crashes mid-write, the log replays and nothing is lost. PostgreSQL calls it WAL literally (&lt;code&gt;pg_wal&lt;/code&gt;), MySQL calls it the redo log, SQLite has a WAL mode. The price is that a WAL is write-optimized and read-terrible: it's a chronological dump, and finding the current state of anything means replaying history.&lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;work journal&lt;/strong&gt; is a WAL: an append-only file the agent writes during execution, cheap and raw, full of detail nobody will ever reread in full.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compaction.&lt;/strong&gt; Log-structured engines (RocksDB, Cassandra) periodically merge their raw files: overwritten values get dropped, duplicates collapse, and what remains is small and fast to read. Kafka literally calls its variant "log compaction". Our &lt;strong&gt;distillation step&lt;/strong&gt; is compaction: the raw journal (or the raw session) gets melted into current rules, and the journal is archived.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The compacted store.&lt;/strong&gt; Here it's a file named &lt;code&gt;POST_MORTEM.md&lt;/code&gt;, in honor of the SRE practice of blameless incident post-mortems. Purists will note that a classic post-mortem covers one incident, while this file is cumulative, more like the project's case law. Same spirit though: root causes, repair guides, action items, no blame. It holds four kinds of records: architectural decisions with their &lt;em&gt;why&lt;/em&gt;, numbered Production Guardrails, pitfalls with repair guides, and a ledger of consciously deferred tech debt.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Databases&lt;/th&gt;
&lt;th&gt;This workflow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WAL: fast append during work, raw, chronological&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docs/worklog/&amp;lt;date&amp;gt;-&amp;lt;plan&amp;gt;.md&lt;/code&gt;, written as the agent works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compaction: merge the log into a compact, read-optimized store&lt;/td&gt;
&lt;td&gt;Distillation prompt at the end of a plan or session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The compacted store, what readers actually query&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;POST_MORTEM.md&lt;/code&gt;: guardrails, pitfalls, tech debt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old log segments deleted after compaction&lt;/td&gt;
&lt;td&gt;Journal archived; git keeps the history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One more file ties the loop together. The agent's auto-loaded entry point (&lt;code&gt;CLAUDE.md&lt;/code&gt; for Claude Code) stays thin: project description, commands, and one hard pointer as its first line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 &lt;strong&gt;IMPORTANT: Before writing any new code, adding features, or refactoring, you MUST first read &lt;code&gt;POST_MORTEM.md&lt;/code&gt; and strictly follow the architectural rules and Production Guardrails recorded there.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;💡 That single line turns &lt;code&gt;POST_MORTEM.md&lt;/code&gt; from documentation the agent might stumble upon into a precondition for writing code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The lifecycle at a glance
&lt;/h2&gt;

&lt;p&gt;Two modes, three prompts total:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Planned work&lt;/th&gt;
&lt;th&gt;Spontaneous work&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Phase 0&lt;/td&gt;
&lt;td&gt;Plan with a task dependency graph&lt;/td&gt;
&lt;td&gt;(no plan; work just happens)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 1: Run → Log&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Prompt 1&lt;/strong&gt;: one item at a time, WAL entry after each&lt;/td&gt;
&lt;td&gt;the session context itself plays the role of the log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 2: Distill&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Prompt 2&lt;/strong&gt;: compact the WAL into &lt;code&gt;POST_MORTEM.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Prompt 3&lt;/strong&gt;: distill straight from session context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both modes are shown below on real tasks from this store, with the prompts in full.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 1: a planned refactor
&lt;/h2&gt;

&lt;p&gt;The best showcase of the planned mode grew out of the ugliest bug in this project's history. Every page kept its server-fetched data in a module-level store object. A module in Next.js initializes once per process, so that object was silently shared between all requests and all users. When one visitor hit an error, the error state stuck, and every visitor after them saw the error page until restart. (The full detective story is a later part of this series.)&lt;/p&gt;

&lt;p&gt;The fix couldn't be a one-liner: introduce a per-request store provider, migrate every page and component to it, split client-only UI state into its own store, delete two dead abstractions along the way. A dozen-plus interdependent items, which is work that needs a plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 0: a plan with a dependency graph
&lt;/h3&gt;

&lt;p&gt;For planned work I don't start from a prompt at all. I use the &lt;a href="https://github.com/obra/superpowers" rel="noopener noreferrer"&gt;Superpowers&lt;/a&gt; plugin for Claude Code, a community skill library that forces a brainstorm → design → implementation-plan pipeline before any code gets written. Any planning approach works. The output that matters for this workflow is a plan whose appendix contains a task dependency graph: which items block which, and which are independent quick wins.&lt;/p&gt;

&lt;p&gt;That graph is what makes strictly iterative execution possible: at any moment the agent knows what one atomic, unblocked item looks like. For the refactor above, the store factory had to exist before any page could migrate to it, but the pages could migrate independently of each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: run and log
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Prompt 1: execute the plan
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an autonomous AI coding agent. Your task is to execute the
attached implementation plan step by step.

So that the codebase and your memory evolve together, we will use the
practice "Run -&amp;gt; Log -&amp;gt; Distill -&amp;gt; Repeat". Your goal is not just to
write code, but to log the process and accumulate experience in the
work journal file `docs/worklog/&amp;lt;date&amp;gt;-&amp;lt;plan-name&amp;gt;.md`.

### RULES AND ALGORITHM:
1. Study the Appendix (the task dependency graph) at the end of the
   plan. You may not start a child task until its parents are done.
2. Work strictly iteratively: ONE specific item per ONE step. Do not
   attempt several tasks at once.
3. The algorithm for each item:
   a) [Maker] Write or change the code for the current isolated task.
   b) [Verifier] Run &amp;lt;verify command&amp;gt; and confirm nothing is broken.
   c) [Distill] Pause and append an entry to the work journal.
   d) The step is complete. Send me a short report and wait for my
      approval before moving to the next item.

### JOURNAL FORMAT:
After each completed item, append an entry to the journal, strictly
following these 5 criteria:
1. [Verified Facts] Which item is done, which files changed, which
   checks passed.
2. [What Worked] Successful patterns and approaches worth repeating.
3. [Distilled Rules] Provisional rules the next steps must respect
   (imports, dependency injection, state management).
4. [Pitfalls &amp;amp; What to Avoid] Compilation errors, dependency conflicts
   or bugs you hit on this item, and how you worked around them.
5. [What's Next] The next atomic task from the plan and its
   prerequisites.

Start with the first unblocked item (the quick wins, if the plan marks
them). What is your first concrete task? Describe what exactly you are
about to change in the code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Fill in:&lt;/strong&gt; &lt;code&gt;&amp;lt;verify command&amp;gt;&lt;/code&gt; (your test or build command: &lt;code&gt;npm test&lt;/code&gt;, &lt;code&gt;flutter test&lt;/code&gt;, &lt;code&gt;cargo check&lt;/code&gt;), the journal path, and attach or reference the plan itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The prompt forces a rhythm: code, verify, log, report, wait. The waiting is there so that an agent drifting off course gets caught after one item instead of after ten. The logging happens right after verification, while the dead ends are still in the context window, because a diff read tomorrow won't tell you why any of it happened. And nothing enters the journal until the checks pass; that rule alone separates a work log from a chat transcript.&lt;/p&gt;

&lt;p&gt;Here's an entry from that refactor's run (lightly reconstructed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### Item 7: migrate category page to the per-request store&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; [Verified Facts] Item 7 done. Changed: category/[id]/page.tsx.
   &lt;span class="sb"&gt;`npm run typecheck`&lt;/span&gt; clean, smoke test green.
&lt;span class="p"&gt;2.&lt;/span&gt; [What Worked] Same pattern as items 5-6: the page fetches data and
   passes it as initState to ProductsStoreProvider; components read
   via useProductsStore(selector).
&lt;span class="p"&gt;3.&lt;/span&gt; [Distilled Rules] Selectors must return scalars or stable
   references. An object literal inside a selector caused an infinite
   re-render loop on item 6; don't repeat it here or on item 8.
&lt;span class="p"&gt;4.&lt;/span&gt; [Pitfalls &amp;amp; What to Avoid] Keystone query results are class
   instances; passing them directly into a client component triggers
   "Only plain objects can be passed to Client Components". Wrap them
   in toPlainObject() before they cross the RSC boundary.
&lt;span class="p"&gt;5.&lt;/span&gt; [What's Next] Item 8 (product page) was blocked by this item; now
   unblocked. Prerequisite: store factory (item 2, done).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entries 3 and 4 are the valuable ones: a rule and a trap caught mid-run, written while the context was hot, phrased as advice for the next item. This is the raw material compaction feeds on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: compaction
&lt;/h3&gt;

&lt;p&gt;When the last item is done, I run the second prompt.&lt;/p&gt;

&lt;h4&gt;
  
  
  Prompt 2: compact the journal
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We have fully completed the plan! All tests pass. Our final task is a
total distillation of the experience, to capitalize it for the long
term.

Melt the work journal (docs/worklog/&amp;lt;date&amp;gt;-&amp;lt;plan-name&amp;gt;.md) down into
our architectural memory, and make sure `CLAUDE.md` remains a clean
entry point. Do not limit yourself to 5-7 bullet points: lay out every
important technical insight and rule, with no length limit.

Perform two actions, strictly in this order:

### ACTION 1: Create or extend `POST_MORTEM.md`
Record the codebase's Book of Law in the project root. Add a section
for this run, labeled with the work type ([feature] / [refactor] /
[redesign] / [deploy] / [debug] / [mixed]), and integrate its lessons
into the global sections:
1. ARCHITECTURAL CHANGES: what changed in the structure of the
   codebase and why. Justify each decision so a future session doesn't
   undo it.
2. PRODUCTION GUARDRAILS: the rules for writing code that
   crystallized out of this run (imports, state management, where
   mutable state is categorically forbidden, etc.). Phrase them as
   hard, numbered standards; justify every rule.
3. PITFALLS &amp;amp; TROUBLESHOOTING: every trap we hit, with a repair guide
   for each in case it recurs: symptom, root cause, the exact
   diagnostic commands.
4. FUTURE BACKLOG: a numbered ledger of deferred tech debt and areas
   needing further work.

### ACTION 2: Verify the entry point `CLAUDE.md`
Remove any temporary statuses, logs, or plan steps that leaked into
`CLAUDE.md`. It must contain ONLY:
1. A short project description.
2. Basic commands (run, test, build).
3. This hard pointer on the first line:
"👉 IMPORTANT: Before writing any new code, adding features, or
refactoring, you MUST first read POST_MORTEM.md and strictly follow
the architectural rules and Production Guardrails recorded there."

Apply the changes, archive the work journal, and show me the structure
of the resulting POST_MORTEM.md.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compaction of that refactor's journal produced the first entries of the project's case law. Two of them:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1. It is FORBIDDEN to declare a mutable module-level object in server components / server actions.&lt;/strong&gt; A module in Next.js initializes once per process: such an object is shared between ALL requests and ALL users. Any per-request state must be created inside the component function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Server data reaches the client ONLY two ways:&lt;/strong&gt; (a) a per-request store created by the page with its data as &lt;code&gt;initState&lt;/code&gt;; (b) plain props. A global module-level store for server data is FORBIDDEN, as are "hydrator" components calling &lt;code&gt;setState&lt;/code&gt; during render.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both came out numbered and imperative, with the reasoning attached, and that formatting does real work. Agents obey "FORBIDDEN / MUST" far more reliably than "prefer / consider", and numbers make rules citable: the agent cites them in code comments, and I cite them in reviews ("this violates guardrail 3").&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 2: a spontaneous session
&lt;/h2&gt;

&lt;p&gt;Everything above assumes a plan existed. Plenty of sessions don't have one, and the best example here is the day of this store's first production deploy.&lt;/p&gt;

&lt;p&gt;The plan looked trivial: push the admin-panel image to Google Cloud Run, run migrations against the managed Postgres (Neon), done. Instead, the container died on startup with the most generic error a platform can produce: "Container failed to start and listen on PORT=8080". No stack trace, and everything ran green locally. Three hours of digging later, the story emerged: the Alpine-based image shipped Prisma engines linked against OpenSSL 1.1, Node 20 links OpenSSL 3, and the query engine segfaulted during the TLS handshake with the production database. Local Postgres has no TLS, so no local run could have caught it. The eventual fix was one line in the Dockerfile; the three hours were spent finding that line.&lt;/p&gt;

&lt;p&gt;No plan predicted any of this, so no journal was being kept. Sessions like this are common (the quick fix that grows into a refactor, the deploy that turns into a debugging saga), and they produce the most valuable lessons, because nothing about them was predicted.&lt;/p&gt;

&lt;p&gt;For these sessions there's a third prompt, run once at the end.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Prompt 3 distills from the live context window, so it has to run &lt;strong&gt;before the session closes&lt;/strong&gt;. Next morning the context is gone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Prompt 3: distill a spontaneous session
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We've finished a working session in which we completed a number of
varied tasks and ad-hoc code fixes without rigid upfront planning. Now
we need to distill and capitalize this spontaneous experience following
the "Run -&amp;gt; Log -&amp;gt; Distill" principle (a self-improving system).

Your task: analyze the entire current session, the history of changed
files, and the context of the bugs we solved, then record that
experience in our architectural memory. Don't limit yourself on length;
dump every important engineering insight.

Perform two actions:

### ACTION 1: Extend/update the file `POST_MORTEM.md`
Find or create `POST_MORTEM.md` in the project root. Carefully
integrate this session's experience into it. Add a section for this
session, labeled with the work type ([feature] / [refactor] /
[redesign] / [deploy] / [debug] / [mixed]), organized into these
blocks:
1. [Architectural changes]: What exactly did we change in the
   structure of the codebase and why (which files we split, which
   dependencies we rewrote, which patterns we applied).
2. [Production Guardrails]: Which new rules for writing code, imports,
   state management, or the design system crystallized out of today's
   changes. Phrase them as hard, numbered standards that must not be
   violated.
3. [Lessons &amp;amp; Pitfalls]: Which non-obvious bugs, compilation errors,
   or library conflicts did we hit today? Why did they happen and how
   did we solve them? Write a detailed repair guide in case they
   recur.
4. [Residual tech debt]: What loose ends did we leave? What from
   today's work needs to be finished, refactored, or covered with
   tests in future sessions.

### ACTION 2: Refresh the entry point `CLAUDE.md`
Make sure `CLAUDE.md` in the project root is clean of process logs,
contains only current build/test commands, and has this hard pointer
on its first line:
"👉 IMPORTANT: Before writing any new code, adding features, or
refactoring, you MUST first read POST_MORTEM.md and strictly follow
the architectural rules and Production Guardrails recorded there."

Do the analysis, update the files, and give me a short summary: which
key rules and pitfalls from this session did you just commit to
memory.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It differs from Prompt 2 in three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The source is the session context, not a journal.&lt;/strong&gt; There's no WAL to compact, so the agent works from what's still in the window: the conversation, the diffs, the bugs solved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It integrates instead of creating.&lt;/strong&gt; Prompt 2 assumes a completed plan and writes a definitive section. Prompt 3 says "find or create" and merges carefully into whatever case law already exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It ends with a ranked summary.&lt;/strong&gt; "Tell me which rules you just committed to memory" forces the agent to prioritize, and gives me a 30-second review of what just entered long-term memory. The human stays editor-in-chief. I occasionally veto a rule that over-generalizes a one-off; a self-improving system with no editor drifts into superstition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run at the end of the deploy saga, this prompt produced the project's most valuable pitfall entry:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;3.12 "Container failed to start on PORT=8080", which was actually a segfault.&lt;/strong&gt; Prisma's musl engines are linked against OpenSSL 1.1; &lt;code&gt;node:20-alpine&lt;/code&gt; ships OpenSSL 3; the query engine SIGSEGVs exactly at the TLS handshake with the production database. Migrations still work (separate process!), local runs are green (local Postgres has no TLS!). &lt;strong&gt;Diagnosis:&lt;/strong&gt; run the same image locally with the production &lt;code&gt;DATABASE_URL&lt;/code&gt; and read the full stderr. &lt;strong&gt;Fix:&lt;/strong&gt; &lt;code&gt;node:20-slim&lt;/code&gt;. &lt;strong&gt;Lesson:&lt;/strong&gt; "works on my machine" must include production-mode TLS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The repair guide is deliberately a command: the next session can rerun the diagnosis in ten seconds instead of rediscovering it. A session later the lesson got generalized into guardrail 18: any image that talks TLS to a database in production gets tested against a TLS database before deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop closes
&lt;/h2&gt;

&lt;p&gt;The payoff came from a third task. Early on, the storefront called the supplier's B2B API during page rendering. One day the supplier API got flaky and took the whole storefront down with it. The distilled lesson became guardrail 4: external APIs are best-effort only, sync never sits on the render path, &lt;code&gt;try { sync } catch { log }&lt;/code&gt;, then serve data from our own DB.&lt;/p&gt;

&lt;p&gt;Sessions later, the agent was writing a new data-access function, and this comment appeared in the diff, unprompted:&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;// Pure DB read: the supplier sync is NOT called here - pages are statically&lt;/span&gt;
&lt;span class="c1"&gt;// cached (ISR), so external APIs must stay off the render path (Guardrail 4,&lt;/span&gt;
&lt;span class="c1"&gt;// tech-debt #9). Sync runs on demand via POST /api/sync-supplier.&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;getProducts&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 rule number, the tech-debt entry that rule closed, and the alternative chosen because of them: the agent is citing the project's own case law while writing new code. This comment did more to convince me the setup works than anything else in five sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't keep the journal in CLAUDE.md
&lt;/h2&gt;

&lt;p&gt;An early version of Prompt 1 told the agent to keep the work journal inside &lt;code&gt;CLAUDE.md&lt;/code&gt;, the same file the agent auto-loads at the start of every session. That's also why Prompt 2 has a "clean up &lt;code&gt;CLAUDE.md&lt;/code&gt;" action: the cleanup was a symptom of the journal living in the wrong place. The prompts above already contain the fix; the reasoning behind it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The WAL stays out of the auto-loaded context file.&lt;/strong&gt; Otherwise every session pays tokens for old execution logs, and the entry point gets diluted right when it should stay sharp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One journal file per plan or session&lt;/strong&gt;, like &lt;code&gt;docs/worklog/2026-07-08-per-request-store.md&lt;/code&gt;. Not auto-loaded. Prompt 1 names it explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; holds only commands and pointers.&lt;/strong&gt; Mine is 48 lines: project description, how to run and verify, the hard pointer to &lt;code&gt;POST_MORTEM.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive the journal after compaction.&lt;/strong&gt; Its value has been extracted. Git keeps the raw material if you ever need it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag sessions by work type&lt;/strong&gt;: &lt;code&gt;[feature]&lt;/code&gt;, &lt;code&gt;[refactor]&lt;/code&gt;, &lt;code&gt;[redesign]&lt;/code&gt;, &lt;code&gt;[deploy]&lt;/code&gt;, &lt;code&gt;[debug]&lt;/code&gt;, &lt;code&gt;[mixed]&lt;/code&gt; in the section header. Rules are type-agnostic (a rule born in a redesign binds a refactor just the same), but tags make future compaction of the post-mortem itself much easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The post-mortem grows.&lt;/strong&gt; Mine is ~300 lines after five sessions. At some point old session narratives need compaction of their own: keep the guardrails and pitfalls, collapse the story. Compaction is never finished, in databases or here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules can over-fit.&lt;/strong&gt; A workaround for one library version can calcify into false doctrine. The numbered format helps: rules are easy to find and repeal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's discipline-dependent.&lt;/strong&gt; A good distillation pass takes a few minutes of review, and if you skip it, the knowledge from that session is simply gone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prior art
&lt;/h2&gt;

&lt;p&gt;For digging deeper, the landscape this workflow sits in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://arxiv.org/abs/2309.02427" rel="noopener noreferrer"&gt;CoALA&lt;/a&gt;&lt;/strong&gt; (Sumers et al., 2023): the paper that mapped agent memory onto the psychology terms used above (working, episodic, semantic, procedural).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://arxiv.org/abs/2308.10144" rel="noopener noreferrer"&gt;ExpeL&lt;/a&gt;&lt;/strong&gt; (Zhao et al., 2023): agents that collect their own trajectories and extract natural-language insights from them. The academic version of the compaction step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.cline.bot/prompting/cline-memory-bank" rel="noopener noreferrer"&gt;The Memory Bank pattern&lt;/a&gt;&lt;/strong&gt;: a popular markdown-memory methodology from the AI-IDE world. It solves a neighboring problem: Memory Bank reconstructs project &lt;em&gt;state&lt;/em&gt; (what we're building, where we stopped), while this workflow accumulates &lt;em&gt;lessons&lt;/em&gt; (what must never happen again). The two combine well.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://every.to/guides/compound-engineering" rel="noopener noreferrer"&gt;Compounding engineering&lt;/a&gt;&lt;/strong&gt; (Every): the same philosophy ("each unit of work should make the next one easier") grown into a full Claude Code plugin with a Plan, Work, Review, Compound cycle. A packaged alternative to the three prompts here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://addyosmani.com/blog/self-improving-agents/" rel="noopener noreferrer"&gt;Self-improving coding agents&lt;/a&gt;&lt;/strong&gt; (Addy Osmani, 2026): a survey of the looped variants, &lt;code&gt;learnings.md&lt;/code&gt; files and continuous loops included.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steal it
&lt;/h2&gt;

&lt;p&gt;The whole system is three prompts and one pointer line. Nothing is specific to Claude Code except the &lt;code&gt;CLAUDE.md&lt;/code&gt; filename; Cursor rules, Windsurf rules, or a plain &lt;code&gt;AGENTS.md&lt;/code&gt; work the same way.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put the hard pointer on the first line of your agent's entry-point file.&lt;/li&gt;
&lt;li&gt;Planned work: Prompt 1 to start the run, Prompt 2 when the plan is done.&lt;/li&gt;
&lt;li&gt;Everything else: Prompt 3 at the end of the session, before you close it.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 Start on an existing project today: run Prompt 3 at the end of your next session and you'll have your first guardrails by tonight. Five sessions from now, your agent will be citing them back to you.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;This is part 1 of a build-in-public series. The project this memory system grew on, a full-stack GraphQL store (Next.js 13 + Keystone 6 + PostgreSQL) running in production for $0/month, is what the next parts dissect: the architecture, the deployment, the ISR + live supplier API mechanics.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>promptengineering</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I added a RAG-powered AI chat to my landing page - and it's 100% free</title>
      <dc:creator>vavilov2212</dc:creator>
      <pubDate>Sun, 21 Jun 2026 01:20:29 +0000</pubDate>
      <link>https://dev.to/vavilov2212/i-added-a-rag-powered-ai-chat-to-my-landing-page-and-its-100-free-48jb</link>
      <guid>https://dev.to/vavilov2212/i-added-a-rag-powered-ai-chat-to-my-landing-page-and-its-100-free-48jb</guid>
      <description>&lt;p&gt;I added a free RAG-powered AI chat assistant to the &lt;a href="https://worklog-studio-site.vercel.app/" rel="noopener noreferrer"&gt;Worklog Studio&lt;/a&gt; landing page, built with Next.js, the Gemini API, and deployed on Vercel. Here's what it does and why I bothered.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzq7m93v240j5rg01b18y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzq7m93v240j5rg01b18y.gif" alt="Demo of the AI chat assistant answering a question about Worklog Studio's features" width="760" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;You can explore source code for this project at a public github repo &lt;a href="https://github.com/vavilov2212/worklog_studio_site" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother with a chat assistant
&lt;/h2&gt;

&lt;p&gt;First of all - it's fun! But really, nobody reads large pieces of text anymore. Having a chatbot that knows your product is a way to motivate users to engage with what you're trying to deliver.&lt;/p&gt;

&lt;p&gt;The hard rule I set for myself: the assistant has to admit when it doesn't know something. A chatbot that confidently invents a feature that doesn't exist is worse than no chatbot at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's wired together
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjn5rx8yl9wy3cmb0or4q.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjn5rx8yl9wy3cmb0or4q.png" alt="Worklog Studio AI chat architecture diagram" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A browser sends a question to a Next.js route running on Vercel. That route checks a rate limiter, pulls the most relevant chunks out of a small precomputed file of embeddings, and sends those chunks plus the question to Gemini. The answer streams back the same way it came in.&lt;/p&gt;

&lt;p&gt;Two entry points on the page (an inline box and a header button) share the same chat panel and the same conversation, so switching between them mid-conversation doesn't lose anything.&lt;/p&gt;

&lt;p&gt;There's no vector database, it does not use an app database, and does not need user accounts. Conversation history lives in &lt;code&gt;sessionStorage&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Worklog Studio?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/vavilov2212/wl-studio" rel="noopener noreferrer"&gt;Worklog Studio&lt;/a&gt; is a small desktop time tracker. As of now it's a bare minimum to be useful. Projects, tasks, time entries - you can comment on each entry so you remember what you were actually doing.&lt;/p&gt;

&lt;p&gt;I built it because every existing tracker I tried was either missing something or too cluttered with features I don't need. It's open source, and if you want to poke around or contribute, here's the repo: &lt;a href="https://github.com/vavilov2212/wl-studio" rel="noopener noreferrer"&gt;github.com/vavilov2212/wl-studio&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why it needed a real server
&lt;/h2&gt;

&lt;p&gt;The site used to ship as a static export on GitHub Pages, which is fine for plain content but can't run any backend code. This feature needs one, because the Gemini API key has to stay on the server. Put that key in client-side code and anyone with dev tools open can grab it in five seconds.&lt;/p&gt;

&lt;p&gt;So the site moved to Vercel. It is Next.js app on the same repo, and deploy does not change.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs: nothing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Used for&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;hosting + the API route&lt;/td&gt;
&lt;td&gt;Hobby (free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;Gemini API&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;embeddings + chat&lt;/td&gt;
&lt;td&gt;free tier, no GCP project needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://console.upstash.com/" rel="noopener noreferrer"&gt;Upstash Redis&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;rate limiting&lt;/td&gt;
&lt;td&gt;free tier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Gemini key comes from the consumer AI Studio flow, not the Vertex AI console. I'll get into why that distinction matters in part 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next
&lt;/h2&gt;

&lt;p&gt;I'm splitting this into three posts because there's enough here to get lost in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: the actual RAG pipeline, chunking and embedding your own content and how the retrieval code decides when to say "I don't know" instead of guessing. No vector database involved at this point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: working with the Gemini API specifically, free tier vs. Vertex AI, and what happens when a model is overloaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The site's repo is &lt;a href="https://github.com/vavilov2212/worklog_studio_site" rel="noopener noreferrer"&gt;worklog_studio_site&lt;/a&gt; if you want to follow along, and &lt;a href="https://github.com/vavilov2212/wl-studio" rel="noopener noreferrer"&gt;Worklog Studio&lt;/a&gt; itself is open for contributions if time tracking is your thing.&lt;/p&gt;

&lt;p&gt;Try the assistant yourself: &lt;a href="https://worklog-studio-site.vercel.app/" rel="noopener noreferrer"&gt;worklog-studio-site.vercel.app&lt;/a&gt;. Click "Ask AI" and see what it knows.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>nextjs</category>
      <category>gemini</category>
    </item>
    <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://dev.to/vavilov2212/the-case-of-the-missing-s3-metadata-a-cors-deep-dive-1gag</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/stop-uploading-files-to-s3-the-wrong-way-50le</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/enforce-module-imports-in-fsd-using-eslint-plugin-import-2d72</link>
      <guid>https://dev.to/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&amp;nbsp;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://dev.to/vavilov2212/master-your-prompt-a-practical-process-to-improve-ai-interactions-2jg3</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/from-import-to-use-migrating-sass-the-right-way-in-a-nextjs-project-5h01</link>
      <guid>https://dev.to/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;📚&amp;nbsp;&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://dev.to/vavilov2212/routing-only-specific-subnets-through-vpn-split-tunneling-on-macos-2aig</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/inject-sass-global-variables-from-nextconfigjs-sass-env-variables-3nco</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/sentry-environment-on-client-in-nextjs-2opj</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/windows-installation-wont-work-due-to-unexpected-0x00005-error-how-to-fix-406m</link>
      <guid>https://dev.to/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://dev.to/vavilov2212/svgs-in-react-almost-everything-you-need-to-know-1nb4</link>
      <guid>https://dev.to/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&amp;nbsp;&lt;code&gt;.svg&lt;/code&gt;&amp;nbsp;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;,&amp;nbsp;&lt;code&gt;.jsx&lt;/code&gt;,&amp;nbsp;&lt;code&gt;.ts&lt;/code&gt;, or&amp;nbsp;&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&amp;nbsp;&lt;code&gt;url()&lt;/code&gt;&amp;nbsp;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&amp;nbsp;&lt;code&gt;@svgr/webpack&lt;/code&gt;&amp;nbsp;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>
  </channel>
</rss>
