<?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: Victor Neves</title>
    <description>The latest articles on DEV Community by Victor Neves (@victorneves).</description>
    <link>https://dev.to/victorneves</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%2F215343%2F49ed15f5-fa05-4772-befc-26a965551554.jpeg</url>
      <title>DEV Community: Victor Neves</title>
      <link>https://dev.to/victorneves</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victorneves"/>
    <language>en</language>
    <item>
      <title>Nuxt DevTools Observatory: the observability panels I wished Nuxt DevTools had</title>
      <dc:creator>Victor Neves</dc:creator>
      <pubDate>Wed, 28 Oct 2026 15:47:38 +0000</pubDate>
      <link>https://dev.to/victorneves/nuxt-devtools-observatory-the-observability-panels-i-wished-nuxt-devtools-had-igp</link>
      <guid>https://dev.to/victorneves/nuxt-devtools-observatory-the-observability-panels-i-wished-nuxt-devtools-had-igp</guid>
      <description>&lt;p&gt;Nuxt DevTools is great. But at some point, while debugging real apps, I kept running into the same questions it couldn't answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which &lt;code&gt;useFetch&lt;/code&gt; calls are actually in the critical path of this page?&lt;/li&gt;
&lt;li&gt;Where does this &lt;code&gt;inject()&lt;/code&gt; get its value from, and why is it &lt;code&gt;undefined&lt;/code&gt; here?&lt;/li&gt;
&lt;li&gt;Is that composable leaking a watcher or an interval after the component unmounts?&lt;/li&gt;
&lt;li&gt;Why does this component re-render 40 times?&lt;/li&gt;
&lt;li&gt;What &lt;em&gt;exactly&lt;/em&gt; happened between the route change and the page being ready?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built &lt;strong&gt;Nuxt DevTools Observatory&lt;/strong&gt;, a module that adds deeper runtime observability on top of the official DevTools. It &lt;strong&gt;complements&lt;/strong&gt; Nuxt DevTools, it doesn't replace it.&lt;/p&gt;

&lt;p&gt;👉 Repo: &lt;a href="https://github.com/victorlmneves/nuxt-devtools-observatory" rel="noopener noreferrer"&gt;victorlmneves/nuxt-devtools-observatory&lt;/a&gt;&lt;br&gt;
👉 Docs: &lt;a href="https://nuxt-devtools-observatory.vercel.app" rel="noopener noreferrer"&gt;nuxt-devtools-observatory.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add nuxt-devtools-observatory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;modules&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;nuxt-devtools-observatory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;devtools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enabled&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open Nuxt DevTools and the new tabs show up. That's it. Everything is enabled by default in development, and you can turn individual features off via the &lt;code&gt;observatory&lt;/code&gt; key in &lt;code&gt;nuxt.config.ts&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. useFetch Dashboard
&lt;/h3&gt;

&lt;p&gt;A central view of every &lt;code&gt;useFetch&lt;/code&gt; / &lt;code&gt;useAsyncData&lt;/code&gt; call: key, URL, status, origin (SSR or CSR), payload size, duration, and a &lt;strong&gt;waterfall timeline&lt;/strong&gt;. Server-side timing is captured by a Nitro plugin and sent to the client over the HMR WebSocket, so you see SSR fetches too.&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%2Fz57nk9egu43tlhuspu6w.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%2Fz57nk9egu43tlhuspu6w.png" alt="useFetch Dashboard" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. provide/inject Graph
&lt;/h3&gt;

&lt;p&gt;An interactive graph of your injection topology. Nodes are colour-coded (provides, injects, both, &lt;strong&gt;missing provider&lt;/strong&gt;), and you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inline value inspection for each provided key&lt;/li&gt;
&lt;li&gt;scope badges (&lt;code&gt;global&lt;/code&gt;, &lt;code&gt;layout&lt;/code&gt;, &lt;code&gt;component&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;shadow detection&lt;/strong&gt; when a child re-provides a key an ancestor already provides&lt;/li&gt;
&lt;li&gt;immediate flagging of any &lt;code&gt;inject()&lt;/code&gt; that resolves to &lt;code&gt;undefined&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;jump-to-editor from the detail panel&lt;/li&gt;
&lt;/ul&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%2Fktk13ut1emblcv1luzbi.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%2Fktk13ut1emblcv1luzbi.png" alt="provide/inject Graph" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Composable Tracker
&lt;/h3&gt;

&lt;p&gt;This is the one I use most. It tracks your &lt;code&gt;useXxx()&lt;/code&gt; composables live: returned &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt; and &lt;code&gt;reactive&lt;/code&gt; values, a change history, and global (shared) state detection by comparing object identity across instances.&lt;/p&gt;

&lt;p&gt;Most importantly, it flags &lt;strong&gt;leaks&lt;/strong&gt;: any watcher or interval still active after &lt;code&gt;onUnmounted&lt;/code&gt; fires.&lt;/p&gt;

&lt;p&gt;You can also edit a writable &lt;code&gt;ref&lt;/code&gt; inline and the new value is applied to the live ref in the running app, which is handy for reproducing edge cases without touching code.&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%2Fq8dvgimgud49wmbn2fjw.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%2Fq8dvgimgud49wmbn2fjw.png" alt="Composable Tracker" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Render Heatmap
&lt;/h3&gt;

&lt;p&gt;A component tree colour-coded by render frequency and duration, with a per-component render timeline, trigger keys and a route filter. You can set thresholds (&lt;code&gt;heatmapThresholdCount&lt;/code&gt;, &lt;code&gt;heatmapThresholdTime&lt;/code&gt;) to highlight the offenders, and export/import snapshots as JSON.&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%2F7wxyicexcsho6sthh7fr.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%2F7wxyicexcsho6sthh7fr.png" alt="Render Heatmap" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Pinia Tracker
&lt;/h3&gt;

&lt;p&gt;For each Pinia store: state snapshots, an action/mutation timeline with before/after diffs, dependency edges (which components and composables touch the store) and hydration attribution (&lt;code&gt;nuxt-payload&lt;/code&gt;, &lt;code&gt;persistedstate&lt;/code&gt; or &lt;code&gt;runtime&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi3l9r4ekymyzd8a7w1f0.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%2Fi3l9r4ekymyzd8a7w1f0.png" alt="Pinia Tracker" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Transition Tracker
&lt;/h3&gt;

&lt;p&gt;A live timeline of every &lt;code&gt;&amp;lt;Transition&amp;gt;&lt;/code&gt; lifecycle event with phase, duration, parent component and cancellation state. It's surprisingly useful for finding transitions that never finish because of missing CSS classes, or ones cancelled by rapid toggling.&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%2F1d0257eoh3yqqczjjnpo.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%2F1d0257eoh3yqqczjjnpo.png" alt="Transition Tracker" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  7. Trace Viewer
&lt;/h3&gt;

&lt;p&gt;Per-route traces that capture everything that happened after a navigation, in order: component mounts, real render durations, fetch timing, composable setup, server phases and transitions. You can inspect them as a &lt;strong&gt;Flamegraph&lt;/strong&gt; or a &lt;strong&gt;Waterfall&lt;/strong&gt;, and compare render behaviour across traces to spot regressions.&lt;/p&gt;

&lt;p&gt;It helps answer things like "is this page slow because of &lt;code&gt;setup()&lt;/code&gt;, or because of DOM patching?" and "which &lt;code&gt;useFetch&lt;/code&gt; is blocking the critical path?"&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%2Fv7f4nqmmrre1a4gyzd5m.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%2Fv7f4nqmmrre1a4gyzd5m.png" alt="Trace Viewer" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works (the interesting part)
&lt;/h2&gt;

&lt;p&gt;All instrumentation happens at build time through &lt;strong&gt;Vite transforms&lt;/strong&gt; that wrap calls at the AST/module level before compilation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useFetch&lt;/code&gt; / &lt;code&gt;useAsyncData&lt;/code&gt; are wrapped with a thin shim that records timing and payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provide()&lt;/code&gt; / &lt;code&gt;inject()&lt;/code&gt; are wrapped with versions that carry file and line metadata. At runtime, a &lt;code&gt;findProvider()&lt;/code&gt; walks the &lt;code&gt;instance.parent&lt;/code&gt; chain to find who provided each key.&lt;/li&gt;
&lt;li&gt;Any &lt;code&gt;useX()&lt;/code&gt; call matching Vue's naming convention is wrapped with a tracking shim that watches for new effects and intervals created during setup.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Transition&amp;gt;&lt;/code&gt; is trickier. The Vue 3 template compiler emits direct named imports (&lt;code&gt;import { Transition as _Transition } from "vue"&lt;/code&gt;), which bypass &lt;code&gt;app.component()&lt;/code&gt; entirely. So the module serves a &lt;strong&gt;virtual proxy module&lt;/strong&gt; for &lt;code&gt;vue&lt;/code&gt; that overrides the &lt;code&gt;Transition&lt;/code&gt; export with an instrumented wrapper.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One detail I'm happy with: the transition wrapper deliberately does &lt;strong&gt;not&lt;/strong&gt; wrap &lt;code&gt;onEnter&lt;/code&gt; / &lt;code&gt;onLeave&lt;/code&gt;. Vue inspects their &lt;code&gt;.length&lt;/code&gt; to choose between CSS and JS timing modes, and wrapping them would change that length and silently alter behaviour.&lt;/p&gt;
&lt;h3&gt;
  
  
  Zero cost in production
&lt;/h3&gt;

&lt;p&gt;Instrumentation is dev-only. When &lt;code&gt;import.meta.dev&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, the transforms are skipped entirely, so there's no runtime overhead in production.&lt;/p&gt;
&lt;h2&gt;
  
  
  Things to know
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance in dev:&lt;/strong&gt; since it wraps fetches, composables and lifecycle hooks, very large apps may see a slower DevTools UI or higher memory usage. Disable what you don't need, and use the &lt;code&gt;max*&lt;/code&gt; options (&lt;code&gt;maxFetchEntries&lt;/code&gt;, &lt;code&gt;maxComposableEntries&lt;/code&gt;, etc.) to cap memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR vs SPA:&lt;/strong&gt; &lt;code&gt;instrumentServer&lt;/code&gt; defaults to &lt;code&gt;true&lt;/code&gt; for SSR apps and &lt;code&gt;false&lt;/code&gt; for SPAs, to avoid double registration from the transform running on both builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opt out per call:&lt;/strong&gt; add &lt;code&gt;/* @devtools-ignore */&lt;/code&gt; before any call you don't want instrumented.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* @devtools-ignore */&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetch&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/sensitive&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Known gap:&lt;/strong&gt; the Render Heatmap route filter can't hide persistent components (layouts), since they appear on every route by definition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The repo includes a playground with intentionally broken scenarios: leaky composables, a missing provider, a heavy list that re-renders constantly, and transitions that are broken or cancelled. Clone it and you can see every panel light up without writing any code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Feedback welcome
&lt;/h2&gt;

&lt;p&gt;The project is MIT-licensed and still young. If you try it, I'd love to hear which panels are useful, what's missing, and where it gets in your way. Issues, discussions and PRs are all welcome.&lt;/p&gt;

&lt;p&gt;⭐ &lt;a href="https://github.com/victorlmneves/nuxt-devtools-observatory" rel="noopener noreferrer"&gt;github.com/victorlmneves/nuxt-devtools-observatory&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Vue.js + Vite - How to dynamically inline SVG's without 3rd party libraries</title>
      <dc:creator>Victor Neves</dc:creator>
      <pubDate>Sun, 13 Mar 2022 15:03:20 +0000</pubDate>
      <link>https://dev.to/victorneves/vuejs-how-to-dynamically-inline-svgs-without-3rd-party-libraries-47ng</link>
      <guid>https://dev.to/victorneves/vuejs-how-to-dynamically-inline-svgs-without-3rd-party-libraries-47ng</guid>
      <description>&lt;p&gt;Recently on a project that I'm working on with &lt;strong&gt;Vue 3 + Vite&lt;/strong&gt; and using a &lt;em&gt;Server Driven UI&lt;/em&gt; approach while building a basic link component that can include icons I started researching for tools that could help me, as we are using SVGs and in order to better manipulate them (e.g change the color on hover), we need to inline them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;When working with &lt;em&gt;Server Driven UI&lt;/em&gt; you don't exactly the data that you will gonna render and in this particular case, we don't know which icon will be added, so it's "not possible" to import the asset on the component as there are several icons that can be rendered. Well, it's possible but you would have to import all of them on the component and create a computed method to check the icon name received on the response in order to grab the icon from the list of available options. As you can imagine having several icons is not the best approach for a maintainable system besides you will import all icons even not using them.&lt;/p&gt;

&lt;p&gt;Problem There a 3rd party libraries that can help to dynamically inline SVGs. I have worked with one of them (svg-sprite) on a previous project. It's nice as in terms of syntax you have something like this when you need to inline an SVG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;:xlink:href=&lt;/span&gt;&lt;span class="s"&gt;"checkedIcon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, you pass the name of the icon that you want to render. Pretty easy right? Well, after setup everything up, yes but you will spend time for that, and every time that a new icon is added to the system, you will have to add the icon to a JSON file. As you can see it's almost the same thing as I have described where you have to import every icon on the component, of course in this case you inline an SVG wherever you need as this will gonna be available overall components.&lt;/p&gt;

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

&lt;p&gt;After seeing this tip &lt;code&gt;import shaderString from './shader.glsl?raw'&lt;/code&gt; on &lt;a href="https://vitejs.dev/guide/assets.html#importing-asset-as-string" rel="noopener noreferrer"&gt;Vite docs&lt;/a&gt; I started testing some implementations to see if I could achieve the same result but dynamically. When appending the parameter "?raw" you basically have access to the content of the file, so when used with SVG files you have the code inside and not the file itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;computed&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;url&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;text&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;hasIcon&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;icon&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;ariaLabel&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;externalLink&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;target&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&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;isExternalLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;externalLink&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;displayIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasIcon&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;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icon&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;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_blank&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;ariaText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;linkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;getSvgIcons&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="s2"&gt;`assets/images/icons/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.svg?raw`&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;svgIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;getSvgIcons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&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="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="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
    &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"isExternalLink"&lt;/span&gt;
    &lt;span class="na"&gt;:target=&lt;/span&gt;&lt;span class="s"&gt;"target"&lt;/span&gt;
    &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;
    &lt;span class="na"&gt;:aria-label=&lt;/span&gt;&lt;span class="s"&gt;"ariaText"&lt;/span&gt;
    &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener noreferrer"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base-link"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base-link__item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt;
        &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"displayIcon"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base-link__icon"&lt;/span&gt;
        &lt;span class="na"&gt;v-html=&lt;/span&gt;&lt;span class="s"&gt;"svgIcon"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt;
    &lt;span class="na"&gt;v-else&lt;/span&gt;
    &lt;span class="na"&gt;:to=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;
    &lt;span class="na"&gt;:aria-label=&lt;/span&gt;&lt;span class="s"&gt;"ariaText"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base-link"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base-link__item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt;
        &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"displayIcon"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base-link__icon"&lt;/span&gt;
        &lt;span class="na"&gt;v-html=&lt;/span&gt;&lt;span class="s"&gt;"svgIcon"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>vue</category>
      <category>vite</category>
      <category>svg</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
