<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alois Sečkár</title>
    <description>The latest articles on Forem by Alois Sečkár (@aloisseckar).</description>
    <link>https://forem.com/aloisseckar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1190890%2F4085b787-6249-4c48-b457-f9e915782b00.png</url>
      <title>Forem: Alois Sečkár</title>
      <link>https://forem.com/aloisseckar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aloisseckar"/>
    <language>en</language>
    <item>
      <title>Dafuq did I just installed?</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Wed, 08 Apr 2026 08:31:41 +0000</pubDate>
      <link>https://forem.com/aloisseckar/dafuq-did-i-just-installed-3bch</link>
      <guid>https://forem.com/aloisseckar/dafuq-did-i-just-installed-3bch</guid>
      <description>&lt;p&gt;During the recent &lt;a href="https://thehackernews.com/2026/03/axios-supply-chain-attack-pushes-cross.html" rel="noopener noreferrer"&gt;Axios Supply Chain Attack&lt;/a&gt; I have accidentally observed couple of devs checking whether they should be worried about their projects or not. I have noticed one interesting thing - the level of understanding of what's really going on, what is the nature of this kind of exploit and how to reliably protect yourself from future ones, is lower than it should be. Therefore, I decided to put together a few paragraphs that should help.&lt;/p&gt;

&lt;p&gt;I am far from being a Node.js security guru, yet I believe I have learned a thing or two on my dev journey so far. If you insist on meeting the real expert, allow me to introduce you &lt;a href="https://lirantal.com/" rel="noopener noreferrer"&gt;Liran Tal&lt;/a&gt; and namely his &lt;a href="https://github.com/lirantal/npm-security-best-practices" rel="noopener noreferrer"&gt;Awesome npm Security Best Practices&lt;/a&gt; repo with a huge list of dos and don'ts when dealing with NPM packages. For less scholarly version, you may proceed to my article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened?
&lt;/h2&gt;

&lt;p&gt;This type of cyberattack occurs when hackers gain credentials that allow them posting malicious versions of existing and legit packages. In this case, &lt;a href="https://thehackernews.com/2026/04/unc1069-social-engineering-of-axios.html" rel="noopener noreferrer"&gt;sophisticated social engineering&lt;/a&gt; was used to steal them directly from one of the devs. More common ways are accidental leaking (vibecoders, do you know what your AI coding assistant is committing into public Git repository?) or exfiltrating data through previous virus infections.&lt;/p&gt;

&lt;p&gt;It usually doesn't take long before the malware is discovered by automated checkers, the maintainers are notified, and the infected packages are pulled off NPM. But during this time frame damage can be done.&lt;/p&gt;

&lt;p&gt;The malicious code typically lurks in what is called a &lt;em&gt;postinstall script&lt;/em&gt;. This is a code that is automatically executed once the package gets installed. While there are many legit use-cases for this, it might also become an open door to your system. And hackers know it (while many JS devs don't).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is it happening?
&lt;/h2&gt;

&lt;p&gt;Despite it is quite easy to exploit &lt;code&gt;npm&lt;/code&gt;'s default behavior, it is also not that hard to build a good defense to prevent most of the common attack vectors.&lt;/p&gt;

&lt;p&gt;So, let me first explain why it can happen and then I'll aim you with solid ways of fending this attack off.&lt;/p&gt;

&lt;h3&gt;
  
  
  NPM package resolution
&lt;/h3&gt;

&lt;p&gt;One of the first commands you'd use when working with JavaScript is &lt;code&gt;npm install&lt;/code&gt;. You probably know that it &lt;em&gt;downloads dependencies listed in your &lt;code&gt;package.json&lt;/code&gt; file into local &lt;code&gt;node_modules&lt;/code&gt; directory&lt;/em&gt;. But the whole process is more complex.&lt;/p&gt;

&lt;p&gt;When executed for the first time, it reads through &lt;code&gt;package.json&lt;/code&gt; and resolves listed packages along with their versions. The version may be specified as an exact one, but you can also see caret (&lt;code&gt;^&lt;/code&gt;) or tilde (&lt;code&gt;~&lt;/code&gt;) ones. Those allow &lt;code&gt;npm&lt;/code&gt; to use &lt;em&gt;latest available version&lt;/em&gt; within the given range.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exact vs loose versions
&lt;/h3&gt;

&lt;p&gt;I'll assume you are familiar with &lt;a href="https://docs.npmjs.com/about-semantic-versioning" rel="noopener noreferrer"&gt;semantic versioning&lt;/a&gt; or you will take a break now and find out. The important take is: &lt;strong&gt;It is effectively impossible to predict the exact outcome of a fresh &lt;code&gt;npm install&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I mean, yes, you can &lt;em&gt;pin down&lt;/em&gt; all &lt;strong&gt;your&lt;/strong&gt; (direct) dependences, but:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You will have harder time maintaining security updates in the future&lt;/li&gt;
&lt;li&gt;The first external library you pull in will probably contain some loose dependencies anyway and you won't be able to control all (unless you use &lt;a href="https://docs.npmjs.com/cli/v11/configuring-npm/package-json" rel="noopener noreferrer"&gt;overrides&lt;/a&gt; for each and single one - and get #1 squared)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I initially thought trying to pin down everything should be my goal, now I am far less convinced. Especially if you develop a library-like solution, you don't really want to make your future users dependent on you. Because if you strictly pin your dependencies, each time a vulnerability is uncovered (like couple of times per day), you would need to update your &lt;code&gt;package.json&lt;/code&gt; and release new patch version of your library. With loosen dependencies, tools like &lt;code&gt;npm audit --fix&lt;/code&gt; will be able to bump the required version directly in consumer's lockfile. Not only is this more convenient but it is also faster, because I reckon you are not always online to be of service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated vulnerability
&lt;/h3&gt;

&lt;p&gt;But this comes with a price. If a malicious version of a package exists at the time installation process runs and it is within the allowed range, it will be grabbed, downloaded and if it contains extra scripts, they will be executed. &lt;/p&gt;

&lt;p&gt;It is hard if not impossible to reason with vanilla &lt;code&gt;npm install&lt;/code&gt;. The flow is deterministic, but its scope is almost always too large as real projects contain many dependencies that are dependent on something else themselves. Would you take the time and control everything? Admit it - you just trust the system.&lt;/p&gt;

&lt;p&gt;And the recent events (or the &lt;a href="https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem" rel="noopener noreferrer"&gt;Shai-Hulud attack&lt;/a&gt; last year) showed us the blind trust may be dangerous.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do?
&lt;/h2&gt;

&lt;p&gt;One would say JavaScript installation process is inherently unsafe and you better should just run away. But before you do, here are the three things you can easily do to mitigate most of the risks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disallow postinstall scripts by default
&lt;/h3&gt;

&lt;p&gt;In cybersecurity awareness trainings you are taught not to click any suspicious links. Yet &lt;code&gt;npm&lt;/code&gt; happily runs any &lt;em&gt;suspicous&lt;/em&gt; script it gets. Unless you tell it not to do so.&lt;/p&gt;

&lt;p&gt;The command is easy. Just use &lt;code&gt;npm install --ignore-scripts&lt;/code&gt; instead of plain install. All of a sudden, no 3rd party scripts will be executed.&lt;/p&gt;

&lt;p&gt;However, it is a double-edged sword, because there might be legit packages that rely on automated &lt;em&gt;postinstall&lt;/em&gt; script and you'll end up in broken state unless you take further manual actions (like &lt;code&gt;npm rebuild &amp;lt;package&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Or, you can solve this in much more elegant way by switching to &lt;a href="https://pnpm.io/" rel="noopener noreferrer"&gt;&lt;code&gt;pnpm&lt;/code&gt;&lt;/a&gt; (which you should absolutely do for a number of other reasons as well). When &lt;code&gt;pnpm&lt;/code&gt; runs &lt;code&gt;install&lt;/code&gt; command, it never allows any scripts by default and only white-list those you allow via &lt;a href="https://pnpm.io/cli/approve-builds" rel="noopener noreferrer"&gt;&lt;code&gt;pnpm approve-builds&lt;/code&gt;&lt;/a&gt; manually or specify through &lt;a href="https://pnpm.io/settings#onlybuiltdependencies" rel="noopener noreferrer"&gt;&lt;code&gt;onlyBuiltDependencies&lt;/code&gt;&lt;/a&gt; setting.&lt;/p&gt;

&lt;p&gt;Either way, you should absolutely limit what is being executed and do not put yourself to the mercy of 3rd party package creators.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit your lockfiles
&lt;/h3&gt;

&lt;p&gt;I remember having a conversation with one of my former colleague: &lt;em&gt;"Oh, you don't commit &lt;code&gt;package-lock.json&lt;/code&gt; into Git repository. It is big, it keeps changing and people keep creating conflicts by (accidentally) changing this file."&lt;/em&gt; I was taught differently back then and I insisted: &lt;em&gt;My project will have the lockfile committed.&lt;/em&gt;" After a few years, I am now pretty sure I was right.&lt;/p&gt;

&lt;p&gt;The so called &lt;em&gt;"lockfile"&lt;/em&gt; is created when the package manager runs &lt;code&gt;install&lt;/code&gt; for the first time and gets updated when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;some (direct) dependency was changed in &lt;code&gt;package.json&lt;/code&gt; since last installation (the most common and expected way)&lt;/li&gt;
&lt;li&gt;you run &lt;code&gt;npm install &amp;lt;package&amp;gt;&lt;/code&gt; or &lt;code&gt;npm uninstall &amp;lt;package&amp;gt;&lt;/code&gt; (effectively the same as #1)&lt;/li&gt;
&lt;li&gt;you run &lt;code&gt;npm update&lt;/code&gt; (this re-resolves loosen caret/tilde deps) or &lt;code&gt;npm audit --fix&lt;/code&gt; (this auto-patches known vulnerable packages, if prescribed version ranges allow it)&lt;/li&gt;
&lt;li&gt;the package manager version is different (the unwanted source of confusion and conflicts around the dev teams)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Otherwise, you will end up with guaranteed "bill of materials", that doesn't change between builds. Not only this helps to reduce infamous &lt;em&gt;"it works on my machine"&lt;/em&gt; bugs, but it also fully prevents sudden changes in the supply chain.&lt;/p&gt;

&lt;p&gt;If you don't include lockfile in your repository, everytime the repo is pulled (manually or inside an automated pipeline), new fresh &lt;code&gt;npm install&lt;/code&gt; runs and up-to-date dependencies are re-resolved. The final build will be almost always different from previous one.&lt;/p&gt;

&lt;p&gt;To be even safer (especially in pipelines), you can switch from using &lt;code&gt;npm install&lt;/code&gt; to &lt;code&gt;npm ci&lt;/code&gt; (aka "continuous integration") - a command that works mostly the same but completely respects existing lockfile and doesn't ever try to change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install with cooldown
&lt;/h3&gt;

&lt;p&gt;The above is good, but if you're unlucky enough, you will create/update lockfile exactly during the ongoing supply chain crisis.&lt;/p&gt;

&lt;p&gt;Fortunately, such attacks are usually short-lived. In the latest Axios case, it only took &lt;a href="https://snyk.io/blog/axios-npm-package-compromised-supply-chain-attack-delivers-cross-platform/#timeline" rel="noopener noreferrer"&gt;like 6 minutes&lt;/a&gt; before the compromised package version was discovered and totally 3 and half hours to take it off. It is very unlikely some malicious package can survive longer than let's say a week. If we can establish a rule that every package version we install must be at least a week old, we would easily prevent 99.9% of such incidents.&lt;/p&gt;

&lt;p&gt;Good news everyone! It is possible with &lt;code&gt;npm&lt;/code&gt;. However, there are buts. &lt;/p&gt;

&lt;p&gt;If you already updated to newest &lt;code&gt;v11&lt;/code&gt;, you were gifted with &lt;a href="https://docs.npmjs.com/cli/v11/using-npm/config#min-release-age" rel="noopener noreferrer"&gt;&lt;code&gt;minimum-release-age&lt;/code&gt;&lt;/a&gt; flag. This allows you to specify the minimal number of days that had to pass between package release date and today. Newer versions will be rejected (silently if there is older fallback allowed by the version constraints and with failure if some exact version is requested).&lt;/p&gt;

&lt;p&gt;Versions &lt;code&gt;v10&lt;/code&gt; and earlier had to rely on slightly less ideal &lt;a href="https://docs.npmjs.com/cli/v10/using-npm/config#before" rel="noopener noreferrer"&gt;&lt;code&gt;before&lt;/code&gt;&lt;/a&gt; where &lt;code&gt;YYYY-MM-DD&lt;/code&gt; date string is passed. This might get tricky to allow rolling dates and you had to do some &lt;a href="https://github.com/lirantal/npm-security-best-practices?tab=readme-ov-file#2-install-with-cooldown" rel="noopener noreferrer"&gt;scripting magic&lt;/a&gt; to achieve it.&lt;/p&gt;

&lt;p&gt;Again, this will protect you from newly published malware, but it will also cut you off from desired updates (like urgent security patches).&lt;/p&gt;

&lt;p&gt;And again, I cannot but advise you to switch to &lt;code&gt;pnpm&lt;/code&gt;, because it naturally combines &lt;a href="https://pnpm.io/settings#minimumreleaseage" rel="noopener noreferrer"&gt;&lt;code&gt;minimumReleaseAge&lt;/code&gt;&lt;/a&gt; with &lt;a href="https://pnpm.io/settings#minimumreleaseageexclude" rel="noopener noreferrer"&gt;&lt;code&gt;minimumReleaseAgeExclude&lt;/code&gt;&lt;/a&gt; to solve exactly this problem.&lt;/p&gt;

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

&lt;p&gt;I am not promising following the advice above will always keep you safe. The topic of Node.js security is complex, and hackers are creative. But many exploits occur only because someone was unaware/lazy/hasty and left a plain &lt;code&gt;npm install&lt;/code&gt; without taking at least minimum precaution.&lt;/p&gt;

&lt;p&gt;Yes. If this is new for you, you may find it confusing and overwhelming. The working pipeline suddenly broke. You eagerly tried to use &lt;code&gt;pnpm&lt;/code&gt; but the container image you use for builds is not knowing it (&lt;code&gt;RUN npm install -g pnpm&lt;/code&gt; to the rescue). You imposed cooldowns and installation log exploded with errors. You banished post-install scripts and now your app won't start. But all the issues can be solved, and you will be rewarded with better sleep knowing you don't have to freak out every time new cyberattack is announced.&lt;/p&gt;

&lt;p&gt;If you have questions and remarks, feel free to share your thoughts in the comments section below. Maybe you know something that I don't and I will be more than happy to find out 👀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>npm</category>
      <category>security</category>
      <category>learning</category>
    </item>
    <item>
      <title>New in Vue - March 2026</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 28 Mar 2026 11:49:43 +0000</pubDate>
      <link>https://forem.com/aloisseckar/new-in-vue-march-2025-2g74</link>
      <guid>https://forem.com/aloisseckar/new-in-vue-march-2025-2g74</guid>
      <description>&lt;p&gt;I &lt;a href="https://dev.to/aloisseckar/new-in-vue-february-2025-2kac"&gt;promissed&lt;/a&gt; new summary once &lt;a href="https://vuejs.amsterdam/" rel="noopener noreferrer"&gt;VueJS Amsterdam&lt;/a&gt; is over. It took me two weeks longer, but there we are. Hear, hear! I bring the latest Vue news.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsywjp8b1lrbu95xywgi.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%2Fcsywjp8b1lrbu95xywgi.png" alt="New Vue newsletter launched" width="66" height="100"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Unfortunately, I must start with a sad announcement - the &lt;strong&gt;Weekly Vue News&lt;/strong&gt;, the invaluable source of inspiration for Vue devs are no more. Their long-time curator Michael Hoffmann &lt;a href="https://weekly-vue.news/issues/v2/205" rel="noopener noreferrer"&gt;called it quits&lt;/a&gt;. Let his hard work be remembered.&lt;/p&gt;

&lt;p&gt;But life goes on. And the life in the Vue ecosystem is everything but steady. So lets recap the new releases:&lt;/p&gt;

&lt;p&gt;The most significant one would be the &lt;a href="https://vite.dev/blog/announcing-vite8" rel="noopener noreferrer"&gt;&lt;strong&gt;Vite 8.0&lt;/strong&gt;&lt;/a&gt;. Why? Because Vite is nowadays fundament for modern JS development. And v8 brings important updates like using brand new and super-fast &lt;a href="https://rolldown.rs/" rel="noopener noreferrer"&gt;Rolldown&lt;/a&gt; bundler. There was a beta testing phase running since December, and now the stable version was made available.&lt;/p&gt;

&lt;p&gt;On top of that, there is &lt;a href="https://voidzero.dev/posts/announcing-vite-plus-alpha" rel="noopener noreferrer"&gt;&lt;strong&gt;Vite+&lt;/strong&gt;&lt;/a&gt; - a unified modern toolchain built with the latest available features as &lt;em&gt;"This is the way"&lt;/em&gt; of developing webapps in 2026. The current state of the project is Alpha with stable release expected soon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vitest&lt;/strong&gt;, the testing framework, couldn't stay behind and &lt;a href="https://vitest.dev/blog/vitest-4-1.html" rel="noopener noreferrer"&gt;released v4.1&lt;/a&gt;. Unlike the significant v4 update, this is rather a service update to natively support Vite v8. But there are also some new features to give a try. If you don't use Vitest in your project, you should think about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nuxt&lt;/strong&gt;, the #1 framework to go with Vue projects, continues its steady evolution, with &lt;a href="https://nuxt.com/blog/v4-4" rel="noopener noreferrer"&gt;v4.4&lt;/a&gt; being the latest release. With each minor version something new appears, although the changes are not really revolutionary. The same applies for &lt;strong&gt;Nuxt UI&lt;/strong&gt;, the official UI library - the &lt;a href="https://github.com/nuxt/ui/releases/tag/v4.5.0" rel="noopener noreferrer"&gt;v4.5&lt;/a&gt; and &lt;a href="https://github.com/nuxt/ui/releases/tag/v4.6.0" rel="noopener noreferrer"&gt;v4.6&lt;/a&gt; were released since my last newsletter. For smaller projects, you really don't need anything else to handle the appearance and basic UX.&lt;/p&gt;

&lt;p&gt;Nuxt already foresees its version 5. You can even opt in into future forward compatibility &lt;a href="https://nuxt.com/docs/4.x/guide/going-further/features#compatibilityversion" rel="noopener noreferrer"&gt;through a setting&lt;/a&gt;. One of the prerequisites is the finalization of &lt;strong&gt;Nitro v3&lt;/strong&gt; JS server engine. This milestone is now one step closer as its &lt;a href="https://nitro.build/blog/v3-beta" rel="noopener noreferrer"&gt;beta phase has started&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Among new tools, I want to point out first stable release of &lt;a href="https://pinia-colada.esm.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;Pinia Colada&lt;/strong&gt;&lt;/a&gt;, the advanced Vue solution for data fetching and state management. You probably have heard of Pinia, this brings the concept further created by the same author.&lt;/p&gt;

&lt;p&gt;I must not forget about &lt;a href="https://www.evlog.dev/" rel="noopener noreferrer"&gt;evlog&lt;/a&gt;, the new logging tool by Hugo Richard, one of the Nuxt team members. I haven't touched it yet, but I really want to do it soon. I really like the idea of having my logs better organized. Logging is generally hard - it doesn't matter until it's too late. Right?&lt;/p&gt;

&lt;p&gt;If you are trying to keep up with the times and use AI to boost your developer performance (which you absolutely should), this might be useful for you - Daniel Kelly from VueSchool wrote an article about &lt;a href="https://vueschool.io/articles/vuejs-tutorials/vue-agent-skills-for-reliable-ai-development/" rel="noopener noreferrer"&gt;Vue Agent skills&lt;/a&gt; that should gradually improve the quality of GenAI tools output.&lt;/p&gt;

&lt;p&gt;Last but not least, we have a &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/" rel="noopener noreferrer"&gt;new TypeScript version&lt;/a&gt;. Microsoft released its stable version 5 days ago. However, this version is considered rather a less important bridge on the way to v7, which is expected to be the real change after its core being rewritten to &lt;code&gt;Go&lt;/code&gt; language for better performance. So you don't necessarily need to drop everything and start migrating your codebases to v6.&lt;/p&gt;

&lt;p&gt;That's it for today. When I approach you the next time, I hope I would be able to share details about the &lt;a href="https://pragvue.com/" rel="noopener noreferrer"&gt;PragVue 2026&lt;/a&gt; conference we are about to organize for the third time already. Until then.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; fixed title from March 2025 to 2026. Thanks &lt;strong&gt;Arvind PJ&lt;/strong&gt; for spotting out.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Upcoming events&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vueconf.us/" rel="noopener noreferrer"&gt;Vueconf.US 2026&lt;/a&gt; - &lt;strong&gt;19-21 May 2026&lt;/strong&gt;, Atlanta [USA]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://madvue.es/" rel="noopener noreferrer"&gt;MadVue 2026&lt;/a&gt; - &lt;strong&gt;22 May 2026&lt;/strong&gt;, Madrid [ESP]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Past issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-february-2025-2kac"&gt;February 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-december-2025-1hk0"&gt;December 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-november-2025-1l1b"&gt;November 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-october-2025-6i7"&gt;October 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-september-2025-4mef"&gt;September 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-august-2025-lip"&gt;August 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-july-2025-24id"&gt;July 2025&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/aloisseckar"&gt;Follow me&lt;/a&gt; for more updates on Vue, Nuxt, Vite - and maybe even more!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>nuxt</category>
      <category>vite</category>
    </item>
    <item>
      <title>Speed of Vibes: We can turn bathroom ideas into features within minutes now</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 14 Feb 2026 21:15:44 +0000</pubDate>
      <link>https://forem.com/aloisseckar/speed-of-vibes-we-can-turn-bathroom-ideas-into-features-within-minutes-now-3dko</link>
      <guid>https://forem.com/aloisseckar/speed-of-vibes-we-can-turn-bathroom-ideas-into-features-within-minutes-now-3dko</guid>
      <description>&lt;p&gt;A month ago, I &lt;a href="https://dev.to/aloisseckar/ai-the-way-out-of-doubt-2fbg"&gt;wrote&lt;/a&gt; I am a &lt;em&gt;&lt;strong&gt;rational pessimist&lt;/strong&gt;&lt;/em&gt; about AI. Yesterday's dispute with my co-worker about unnecessary complexity of AI-infused &lt;em&gt;Korn shell&lt;/em&gt; script delivered by our other colleague reminded me there are also &lt;em&gt;&lt;strong&gt;radical pessimists&lt;/strong&gt;&lt;/em&gt; among us. However, every day when I dare to go further into the uncertain wilderness of imperfect prompts followed by indeterministic and &lt;em&gt;not-always-right&lt;/em&gt; responses, I see progress, new opportunities and new ways of doing better.&lt;/p&gt;

&lt;p&gt;Today, I have another success story to share.&lt;/p&gt;

&lt;p&gt;It started one evening after a workday recently. I came back from my daily dev job and went running to compensate for hours sitting in front of a screen. After the jogging session I was in a shower and suddenly got a cool idea for my side project.&lt;/p&gt;

&lt;p&gt;I thought it would be nice to provide to my future users a convenient way of reporting bugs. But also to make it convenient for me. I would be happiest to have reports appearing as GitHub issues. But teaching BFUs to login to GitHub and creating new issues in a consistent way? Good luck with that. So what if I just provide a simple form on my website and wire it to a backend service that will collect the input and create the issue automatically?&lt;/p&gt;

&lt;p&gt;Sounded like a plan. But imagine all the work you need to do. Putting together the form, writing the backend service, figuring out how the GitHub API works exactly to be able to call it... Let's just put the idea into the inner memory deep in the back of my brain and we will surely look at it. &lt;em&gt;One day. Not today...&lt;/em&gt; My inner demon of procrastination nearly won again.&lt;/p&gt;

&lt;p&gt;But times have changed lately. Now I am armed and dangerous with tireless AI companions. Despite all those legit complaints about coding agents not doing their job as good as you would hope and not nearly as great as AI-driven companies keep promising you, in early 2026 your favorite excuses &lt;em&gt;"I don't know how to start"&lt;/em&gt; or &lt;em&gt;"This task is too big to start it today"&lt;/em&gt; are no longer valid. You can always just kick off with a simple natural language prompt and see how it goes. Even the last season's truth that the prompt needs to be well designed (and thus you "cannot" start as &lt;em&gt;"I don't know how to write a correct prompt"&lt;/em&gt;) is fading out.&lt;/p&gt;

&lt;p&gt;Before going further, I'd like to make a statement. This &lt;strong&gt;doesn't&lt;/strong&gt; mean I am encouraging you to just fire ambiguous prompt and YOLO-deploy the first iteration right into the production.  &lt;a href="https://ai-manifesto.dev/" rel="noopener noreferrer"&gt;The AI Manifesto&lt;/a&gt; is still valid and &lt;strong&gt;you should always verify the results&lt;/strong&gt;, reason about code and understand everything before moving on. It is easier for me as a senior dev, but if you are a beginner, the more important it is before you get lost and become a hostage of a machine mind. On the other hand, you shouldn't hold yourself back too much. AI is there for you. Use it. Benefit from it. 💪🦾&lt;/p&gt;

&lt;p&gt;Before I finished bathing, ate dinner and socialized with my family, it was already getting quite late. But I was feeling lucky and wanted to try.&lt;/p&gt;

&lt;p&gt;Here is the prompt for my (paid) Copilot I quickly put together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want a new feature in this Nuxt 4 project. I need a new client page 
"/report" with a Nuxt UI form entitled "Report problem" that will include 
text field for "gameLink", radio switching between "report false positive" 
and "report false negative", text field for "issue" and text area 
"description" for describing the issue to be reported. All fields will be 
mandatory. The filled form will be send to backend API route where it is 
validated and then a GitHub issue in &amp;lt;&amp;lt;REPO&amp;gt;&amp;gt; repository is created from 
given contents. Issue will be named by "issue" field and contain both 
"gameLink" and "description". Selected radio value will result into 
corresponding label (that will be prepared in the repositry). The report 
form is annonymous, there will be dedicated service account with access 
token to create the issues configurable via Nuxt runtime config (server-
side, so NOT public).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just this. No context engineering, no MCP servers, no skills or whatever. Just my codebase, internet connection and the rough task description in natural language with a few instructions and restrictions. Typos and grammar mistakes included.&lt;/p&gt;

&lt;p&gt;Maybe one thing. If you are AI-geek, you may have noticed new model &lt;a href="https://www.anthropic.com/news/claude-opus-4-6" rel="noopener noreferrer"&gt;Claude Opus 4.6&lt;/a&gt; was released recently. I did notice and I also decided this was a perfect opportunity to test it in action. And I wasn't disappointed.&lt;/p&gt;

&lt;p&gt;I spent two, maybe three minutes writing the prompt (while already have it more or less ready in my mind). Copilot with Claude took about 4 minutes to deliver the result. The surprising part was - the code was nearly perfect!&lt;/p&gt;

&lt;p&gt;I was trying, but I was unable to find any significant flaws. It created decent frontend form, it added Zod schema validation, it created backend endpoint, handled and validated the incoming data, put together the GitHub API call and wired the response back into the client. It even noticed there was no &lt;code&gt;&amp;lt;NuxtPage /&amp;gt;&lt;/code&gt; yet and made adjustments accordingly to support new &lt;code&gt;/report&lt;/code&gt; page along the existing (implicit) &lt;code&gt;/index&lt;/code&gt;. Yea, I might do some refactoring and polishing later, because looking back, the code might be more compact and less verbose. But overall it was not &lt;em&gt;glued together somehow until it accidentally worked&lt;/em&gt;. It was a decent job. I don't think I would do much better for the first iteration.&lt;/p&gt;

&lt;p&gt;My victory wasn't so flawless, because then I spent at least 30 more minutes configuring my new GitHub service account and getting the proper access token (AI-backed again). But overall it was less than an hour from the first keystroke to test issue successfully created in my repository. Wow! 👀&lt;/p&gt;

&lt;p&gt;There were a few success points that helped:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I knew what and how I want to do and I know &lt;a href="https://nuxt.com/" rel="noopener noreferrer"&gt;Nuxt&lt;/a&gt; good enough to request general architecture with at least basic level of security (i.e. token must be kept on server-side and not exposed as public config).&lt;/li&gt;
&lt;li&gt;Copilot wasn't building from scratch but inside already established repository with configured Nuxt UI and existing server routes to copy from.&lt;/li&gt;
&lt;li&gt;People from Nuxt ecosystem are working hard to create AI-friendly solutions so it is much easier for agents to get relevant up-to-date information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was hoping for good outcome, but this really amazed me. It reminded me that it already me who is being the bottleneck. My hesitation, my reluctance and my incompetence to delegate tasks that should be delegated without losing control. Despite I am still not scared about my very existence as some fellow devs seem to be, I was re-assured I should know much better. And I will keep trying to do so.&lt;/p&gt;

&lt;p&gt;What about you? What are your recent experiences with AI? Feel free to share your stories, questions, objections and concerns in the comments below 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>New in Vue - February 2025</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 07 Feb 2026 08:14:42 +0000</pubDate>
      <link>https://forem.com/aloisseckar/new-in-vue-february-2025-2kac</link>
      <guid>https://forem.com/aloisseckar/new-in-vue-february-2025-2kac</guid>
      <description>&lt;p&gt;As you probably didn't even notice, I skipped January issue of my wannabe monthly newsletter. I was in a hurry towards some deadlines, and I wrote 3 other articles in 2026 already. So I decided not to post some half-baked something last Saturday and to postpone the release. Today I am travelling by train from Prague to Ostrava, which is always a good opportunity for creative work. Here it comes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsywjp8b1lrbu95xywgi.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%2Fcsywjp8b1lrbu95xywgi.png" alt="New Vue newsletter launched" width="66" height="100"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Nuxt began 2026 with a &lt;a href="https://nuxt.com/blog/v4-3" rel="noopener noreferrer"&gt;new minor release &lt;code&gt;4.3&lt;/code&gt;&lt;/a&gt;. Among numerous updates and improvements, it is now possible to opt-in into forthcoming &lt;strong&gt;v5&lt;/strong&gt; features via a setting. Nuxt v5 shouldn't be a revolutionary update with large API and behavior changes, but it will bring completely re-worked &lt;a href="https://v3.nitro.build/" rel="noopener noreferrer"&gt;Nitro v3&lt;/a&gt; server engine promising faster and more effective execution with a number of new cool features. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Patch version &lt;a href="https://github.com/nuxt/nuxt/releases/tag/v4.3.1" rel="noopener noreferrer"&gt;4.3.1&lt;/a&gt; landed just a few hours after I finished my overview.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Owners of larger projects also got a pleasant announcement about the official support for Nuxt v3 being extended by 6 months - from end of January to &lt;strong&gt;31 July 2026&lt;/strong&gt;. That gives everyone more time for migration efforts. Some are complaining that the support period should be even longer as production teams need more stability but remember Nuxt is an open source-project. Despite handful of the team members are now being paid by Vercel, the solution capacity is still limited. I would rather see new features being developed than legacy code being maintained indefinitely for (often non-paying) users.&lt;/p&gt;

&lt;p&gt;There are two new official Nuxt modules around the corner. &lt;a href="https://nuxt.com/modules/a11y" rel="noopener noreferrer"&gt;Nuxt Accessibility&lt;/a&gt; will focus on dealing with accessibility issues by providing ways of testing and giving hints during development. &lt;a href="https://nuxt.com/modules/hints" rel="noopener noreferrer"&gt;Nuxt Hints&lt;/a&gt; will go even beyond that and should provide &lt;em&gt;insights about your applications' performance, accessibility, and security&lt;/em&gt;. Both modules are now in alpha stage towards their v1 release which is expected soon.&lt;/p&gt;

&lt;p&gt;Formerly paid premium feature &lt;a href="https://content.nuxt.com/blog/studio-oss" rel="noopener noreferrer"&gt;Nuxt Studio&lt;/a&gt; was made free and fully open-sourced. The transition process started after &lt;a href="https://vercel.com/blog/nuxtlabs-joins-vercel" rel="noopener noreferrer"&gt;Vercel acquired NuxtLabs&lt;/a&gt; last summer and was finished at the dawn of 2026. Nuxt Studio used to be a way of making money for Nuxt development. As this is not necessary anymore, you have this advanced CMS solution at your disposal now.&lt;/p&gt;

&lt;p&gt;Adam Berecz announced his &lt;a href="https://vueform.com/news/20260108-announcing-vueform-2-0" rel="noopener noreferrer"&gt;Vueform 2.0&lt;/a&gt;. I learned about Vueform 11 months ago from his conference talk and I immideately liked it. Now it seems there will be a paradigm shift from more opinionated form-handling solution to a headless core that will allow smooth extension by almost any UI library. Looking forward to the result. Forms are a big (and somewhat painful) topic, and I am afraid I haven't picked my dream setup yet. Another player out there might be &lt;a href="https://formisch.dev/" rel="noopener noreferrer"&gt;Formisch&lt;/a&gt; which is being developed from last summer by Fabian Hiller, the author of Valibot validation library. But since &lt;a href="https://ui.nuxt.com/" rel="noopener noreferrer"&gt;Nuxt UI&lt;/a&gt; also opensourced their previously paid components, maybe their &lt;a href="https://ui.nuxt.com/docs/components/form" rel="noopener noreferrer"&gt;Form&lt;/a&gt; is all a Nuxter would need? I am planning to experiment on this theory a bit...&lt;/p&gt;

&lt;p&gt;Let's not forget about broader Vite ecosystem. As you may have heard, one of the fundamental parts of the ongoing improvement efforts is &lt;strong&gt;Rolldown&lt;/strong&gt;. The bundler written in Rust is made to be crazy fast and effective in building modern webapps. Vite version 8, which is already in beta stage &lt;a href="https://voidzero.dev/posts/announcing-vite-8-beta" rel="noopener noreferrer"&gt;since December&lt;/a&gt;, will be powered by Rolldown exclusively. And soon there will be standalone Rolldown release, we already have &lt;a href="https://voidzero.dev/posts/announcing-rolldown-rc" rel="noopener noreferrer"&gt;release candidates&lt;/a&gt;. And there is much more going on as you can see in the &lt;a href="https://voidzero.dev/posts/whats-new-jan-2026" rel="noopener noreferrer"&gt;fresh monthly summary&lt;/a&gt; by Alex Lichter.&lt;/p&gt;

&lt;p&gt;Last thing that caught my attention lately is &lt;a href="https://github.com/npmx-dev/npmx.dev" rel="noopener noreferrer"&gt;npmx&lt;/a&gt; - an alternative frontend to NPM package registry. It is an open-source project, currently under active development, that promises improved experience when browsing NPM packages. Not a competing repository and ecosystem, just an alternative frontend with additional features. I was satisfied with classic &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;https://www.npmjs.com/&lt;/a&gt;, but this seems as one of those updates you don't know you need until you get it. I will keep my eye on it.&lt;/p&gt;

&lt;p&gt;I plan to release the March issue after &lt;a href="https://vuejs.amsterdam/" rel="noopener noreferrer"&gt;VueJS Amsterdam conference&lt;/a&gt;. This year I am not travelling there in person, but I will keep my eye on the updates and I am sure there will be a lot of news to talk about. Take care until then!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Upcoming events&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vuejs.amsterdam/" rel="noopener noreferrer"&gt;VueJS Amsterdam 2026&lt;/a&gt; - &lt;strong&gt;12-13 March 2026&lt;/strong&gt;, Amsterdam [NED]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vueconf.us/" rel="noopener noreferrer"&gt;Vueconf.US 2026&lt;/a&gt; - &lt;strong&gt;19-21 May 2026&lt;/strong&gt;, Atlanta [USA]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://madvue.es/" rel="noopener noreferrer"&gt;MadVue 2026&lt;/a&gt; - &lt;strong&gt;22 May 2026&lt;/strong&gt;, Madrid [ESP]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Past issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-december-2025-1hk0"&gt;December 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-november-2025-1l1b"&gt;November 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-october-2025-6i7"&gt;October 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-september-2025-4mef"&gt;September 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-august-2025-lip"&gt;August 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-july-2025-24id"&gt;July 2025&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/aloisseckar"&gt;Follow me&lt;/a&gt; for more updates on Vue, Nuxt, Vite - and maybe even more!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>nuxt</category>
      <category>vite</category>
    </item>
    <item>
      <title>I cheated to maintain GitHub streak and I don't regret it</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 24 Jan 2026 14:13:23 +0000</pubDate>
      <link>https://forem.com/aloisseckar/i-cheated-to-maintain-github-streak-and-i-dont-regret-it-39ka</link>
      <guid>https://forem.com/aloisseckar/i-cheated-to-maintain-github-streak-and-i-dont-regret-it-39ka</guid>
      <description>&lt;p&gt;Since September 2022, I am actively trying to have a GitHub streak - every day at least one new contribution. And yes, I totally agree with you, &lt;a href="https://dev.to/sylwia-lask/your-github-contribution-graph-means-absolutely-nothing-and-heres-why-2kjc"&gt;it is meaningless&lt;/a&gt;. But it has a meaning for me. I want to do it, and I want to keep it. Even if some days the contribution has little to no value - like just adding a new article I have recently read into &lt;a href="https://master-coda.cz/" rel="noopener noreferrer"&gt;one of my news lists&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://github.com/DenverCoder1/github-readme-streak-stats" rel="noopener noreferrer"&gt;this widget&lt;/a&gt; to display the number of days on my GitHub profile. It was great until it stopped working some time ago. Some API changes probably. Maybe it'll come back eventually. But I didn't want to wait. I found &lt;a href="https://github.com/pranesh-2005/github-readme-stats-fast" rel="noopener noreferrer"&gt;an alternative&lt;/a&gt; that works here and now.&lt;/p&gt;

&lt;p&gt;Except it doesn't work the same way as the previous one and as the GitHub commit graph itself. It only counts "real" commits, it doesn't recognize code reviews.&lt;/p&gt;

&lt;p&gt;And this pointed out a flaw in my beloved streak. One day, June 1st, 2024, I somehow forgot to actually commit something. I "only" did two reviews for my Dependabot updates. I didn't notice, because when I opened GitHub page, the day was green. But my streak suddenly dropped by more than half 🥹&lt;/p&gt;

&lt;p&gt;I could have just let it go and accept the bitterness. But I didn't want to. The other day I did four commits! What if I just virtually travel back in time and alter one of them to fill in the gap? 💡&lt;/p&gt;

&lt;p&gt;And so I did. And the timeline was fixed. And I can enjoy my 1236 and counting streak. End of the story.&lt;/p&gt;

&lt;p&gt;But this wasn't meant to be just a confession. And I don't ask for redemption. The title says I don't regret it. Here I wanted to explain why not.&lt;/p&gt;

&lt;p&gt;Because due to this "cheating", I learned to know Git better than before. &lt;/p&gt;

&lt;p&gt;Git is one of those things you can adopt in a few minutes and then spend a lifetime trying to really understand it. Btw it took &lt;a href="https://www.zdnet.com/article/linus-torvalds-built-git-in-10-days-and-never-imagined-it-would-last-20-years/" rel="noopener noreferrer"&gt;just 10 days&lt;/a&gt; to forge this backbone on modern development. Stories like that keep reminding me how mediocre I actually am.&lt;/p&gt;

&lt;p&gt;To be able to change a year and half worth of my project Git history, I needed to understand how to work with &lt;code&gt;git rebase&lt;/code&gt; command. How to find the correct commit and how to alter it via the &lt;em&gt;interactive&lt;/em&gt; mode. The biggest Aha! moment was realizing that once you do this, you effectively &lt;strong&gt;erase the current history&lt;/strong&gt; starting from the altered commit and you'll get a new one. The changes and messages remain the same (except what was changed), but &lt;strong&gt;all commits will become new with new hashes&lt;/strong&gt;. I used &lt;code&gt;git rebase&lt;/code&gt; before, but only to fix very recent issues. So I successfully overlooked this fact. Now I am smarter.&lt;/p&gt;

&lt;p&gt;Another 🤯 thing I have learned is that when using GitHub, there are two commit dates - the original &lt;code&gt;Author Date&lt;/code&gt; (marking the point of time when the commit was created) and &lt;code&gt;Commit Date&lt;/code&gt; (changing when the commit gets edited - or rebased). I learned it the hard way after I &lt;em&gt;force-pushed&lt;/em&gt; my changes to GitHub and suddenly 150 commits were "made" on 17th January 2026 😰 &lt;/p&gt;

&lt;p&gt;That unfortunate event directly leads into my third discovery of existence of &lt;code&gt;git filter-branch&lt;/code&gt; command and (more modern) &lt;code&gt;git filter-repo&lt;/code&gt; tool. Those can be called into action to do bulk updates over commits. And help undone lapses like mine. So no worries, after another &lt;em&gt;trial-and-error&lt;/em&gt; session with Copilot, I managed to repair my flawed Git history, and you could never tell again.&lt;/p&gt;

&lt;p&gt;And that concludes my story. If you already knew all the above, good for you. It took me like 8 years of working with Git to dig that deep. I guess one can go even deeper, but I will take a break for a while. If you have stories to share or questions to ask, feel free to express them in the comments below. And stay tuned for another &lt;em&gt;Alois discovers trivial things&lt;/em&gt; article.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; You should be careful when tampering with your Git timeline and force-pushing something in the repository. If you have changes in other branches or even uncommitted in your local checkouts, you may get into trouble. If the team is bigger than just you, the troubles may be even bigger. I could afford to ignore those concerns as it was my private repo with no WIP. But this is not always the case.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>github</category>
      <category>git</category>
      <category>learning</category>
    </item>
    <item>
      <title>AI the way out of doubt</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 17 Jan 2026 08:17:54 +0000</pubDate>
      <link>https://forem.com/aloisseckar/ai-the-way-out-of-doubt-2fbg</link>
      <guid>https://forem.com/aloisseckar/ai-the-way-out-of-doubt-2fbg</guid>
      <description>&lt;p&gt;When it comes to AI, I believe to be a &lt;em&gt;rational pessimist&lt;/em&gt;. I am using AI-powered tools here and there, but I don't see it as a second coming of Jesus that would take our dev jobs and replace us. It is a useful tool, but only a tool. This is probably why I am always a bit behind the newest features and trends that keep emerging every week.&lt;/p&gt;

&lt;p&gt;On the other hand, I am not reluctant to AI either. I don't think it is crippling my mind not to write everything by hand and rather focus on the bigger picture than wiring all the boilerplate together. But finding the right tasks to be asked about or even delegated to AI-agent seems to be the biggest bottleneck in my workflows. I am being constrained by &lt;em&gt;too much thinking&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;While the other extreme - not thinking and all and just &lt;em&gt;vibe code&lt;/em&gt; the entire app and let it to production without any sanity and security checks   - might be even worse, not shipping anything, because you cannot decide how to put it together, is not the ideal outcome either. Whenever I manage to break from the circle, the positive results surprise me. I decided to write this article to share my latest small win achieved through AI. Maybe it inspires you too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scene
&lt;/h2&gt;

&lt;p&gt;I have a custom page where I collect and keep all my running since 2013 - where I run, how many meters and what time. I am just a casual runner, but  it's becoming decent set - nearly 2000 entries and closing to 12K kilometers. And it all can be tracked down to March 11th 2013, when I started with first 1825 meters in 9 minutes and 15 seconds.&lt;/p&gt;

&lt;p&gt;The website offers both displaying and filtering the records and the simple admin form, so I can add new record just after I stop running from my phone. It was evolving over the time from custom PHP glued to HTML page to being part of a Nuxt application with using my own &lt;a href="https://github.com/AloisSeckar/nuxt-neon" rel="noopener noreferrer"&gt;nuxt-neon module&lt;/a&gt; for database connection.&lt;/p&gt;

&lt;p&gt;It had one big flaw - for simplicity, I placed logic to frontend and then never troubled with moving the database operations to server-side. Because noone really cared about my running data, there was no urge to change it. I was always like &lt;em&gt;"yeah, I should do it"&lt;/em&gt;, but then always like &lt;em&gt;"but not today"&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The process
&lt;/h2&gt;

&lt;p&gt;Last Wednesday was another such day. &lt;/p&gt;

&lt;p&gt;Productive me: _I should do it now. I need some task for daily GitHub streak anyway."&lt;/p&gt;

&lt;p&gt;Lazy me: &lt;em&gt;But I am tired from coding all day at work. And surely some issues will arise and I will have to sort them out till midnight. Nah, let's play videogames instead.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But then for some reason I got that little lightbulb above my head like in cartoons. &lt;strong&gt;What if I just try asking Copilot to refactor my code and see what happens?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I nearly fell in the &lt;em&gt;How to write the correct prompt?&lt;/em&gt; trap, but this time I didn't get distracted. I decided to apply KISS principle and see.&lt;/p&gt;

&lt;p&gt;Therefore, my first prompt was pretty straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refactor "getTracks" and "getRuns" methods by moving them to Nuxt server side 
and expose as API routes under /server/routes/tracks.get.ts 
and /server/routes/runs.post.ts. Preserve the "filter" as POST input.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Few months ago, my newsfeed was full of articles about &lt;code&gt;prompt engineering&lt;/code&gt; or even &lt;code&gt;context engineering&lt;/code&gt; as an emerging must-have skill. But fast-forward to present. Your codebase is the context and even a mere Copilot (with Pro subscription) can understand simple prompts good enough to deliver expected results. I am using &lt;code&gt;Claude Sonet 4.5&lt;/code&gt; model recently, but I didn't really try any comparison with others.&lt;/p&gt;

&lt;p&gt;It was thinking and working for a while and then I got my refactor. Being aware of &lt;a href="https://ai-manifesto.dev/" rel="noopener noreferrer"&gt;The AI Manifesto&lt;/a&gt;, I didn't just accept the &lt;em&gt;something&lt;/em&gt; it spit out. I went through all changes carefully.&lt;/p&gt;

&lt;p&gt;This is a big advantage for us, seasoned devs - we can verify the results by understanding the code. Even if I am using AI to discover &lt;em&gt;how to do XY&lt;/em&gt;, I can follow up to some level and get a basic idea about what is going on. And I can challenge parts I truly don't understand, asking &lt;em&gt;What does this do?&lt;/em&gt; or &lt;em&gt;Why you put it there?&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;This time the code review was easier because I know how to write a Nitro route. I just took liberty not to since now I have a pocket junior dev who can do it for me. I wouldn't be bothering you with implementation details, I have a &lt;a href="https://dev.to/aloisseckar/nuxt-tutorial-0-introduction-4mli"&gt;series of Nuxt tutorials&lt;/a&gt; if you're interested.&lt;/p&gt;

&lt;p&gt;Anyway, the outcome exceeded the expectations. The delivered code was fine. The only issue that Copilot cannot foresee (unless I would ship an MCP server along with my nuxt-neon module) was that I recently split server- and client-side composable, so it must import &lt;code&gt;useNeonServer&lt;/code&gt; instead of &lt;code&gt;useNeonClient&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I fixed that manually. Towards Copilot, I had one additional remark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do we need "useAsyncData"? Shouldn't we be fine with "useFetch"?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was merely a cosmetic thing, the simpler way of achieving the same result - getting data from backend into frontend. And honestly, it was my fault, because I was using &lt;code&gt;useAsyncData&lt;/code&gt; in my code and gullible Copilot just copied it. But he also delivered a fix in no time.&lt;/p&gt;

&lt;p&gt;Reading data was secured at backend. Now I needed to do the same for adding new runs. My third prompt was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Going further. Lets also move "submitRun" method from client side Form.vue 
to new server side /server/routes/runs-add.post.ts and redirect client 
Vueform component to it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was fast including AI picked up change to &lt;code&gt;useNeonServer&lt;/code&gt; and mimicked it correctly. But a technical issue arose - because the data from input form are being posted as &lt;code&gt;multipart/form-data&lt;/code&gt;, standard Nitro &lt;code&gt;readBody&lt;/code&gt; method cannot be used for parsing the request. The correct method handling this case is &lt;code&gt;readFormData&lt;/code&gt;. Copilot helped me debug based on a simple console log and fixed the error itself.&lt;/p&gt;

&lt;p&gt;The last piece was to migrate function for deleting erroneous entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last refactor. Move "deleteRun" from Table.vue to backend to 
/server/run-delete.delete.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would have been a walk in the park, if I didn't misclicked to "undo" instead "keep" button and didn't accidentally erase all the changes 🙈 Fortunately, AI doesn't judge and swiftly re-created the edit.&lt;/p&gt;

&lt;p&gt;I spend some more time testing (and coming up with new refactoring ideas), but the job I was afraid to start was done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The outcome
&lt;/h2&gt;

&lt;p&gt;One evening, less than one hour, and the migration from vulnerable Nuxt frontend to more secured Nitro backend was completed. Most of the time I didn't write code, I was just reading and verifying what Copilot produced. &lt;/p&gt;

&lt;p&gt;The task I didn't want to start was finished before it could bother me. It was fun, it was useful and it moved me forward. Just wonder how many more tasks like this wait ahead. Let's find out.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>refactoring</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Nuxt Tutorial 8 - UI Integrations</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sun, 11 Jan 2026 15:32:27 +0000</pubDate>
      <link>https://forem.com/aloisseckar/nuxt-tutorial-7-ui-integrations-4mjm</link>
      <guid>https://forem.com/aloisseckar/nuxt-tutorial-7-ui-integrations-4mjm</guid>
      <description>&lt;p&gt;In the previous part we dealt with CSS; now we’ll move on to more advanced UI integrations. Just like with styling, you don’t necessarily need to reinvent the wheel. Instead of building every functional element from scratch, it’s usually easier to reach for a dedicated library.&lt;/p&gt;

&lt;p&gt;We’ll go through four practical integration examples. The selection isn’t driven by any deep logic - these are simply technologies I’ve come across and needed them in my Nuxt projects. Still, they demonstrate some general techniques you can reuse if you ever need to integrate something similar. At the end of the article, you’ll also find links to ready-made integrations for other well-known UI libraries from the Vue ecosystem. Let’s dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrap
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; used to be - and may still be - the most popular CSS framework for fast, responsive web development. It includes a set of predefined CSS classes, components, and JS plugins that make it easier to build modern design, responsive layouts, forms, navigation, and other interactive elements. It goes further than the previously covered &lt;a href="https://dev.to/aloisseckar/nuxt-tutorial-7-adopting-css-1m2a#tailwind-css"&gt;Tailwind CSS&lt;/a&gt;, which focuses solely on styling.&lt;/p&gt;

&lt;p&gt;Personally, I never really like it. If you’re starting today, I’d rather recommend combining Tailwind CSS with some library specialized in form controls (one of the next articles will cover those). But many developers are used to Bootstrap from earlier days, and of course Nuxt doesn’t prevent you from continuing to use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuxt integration
&lt;/h3&gt;

&lt;p&gt;You can find some Bootstrap solutions in the Nuxt modules directory, but I had doubts about how up-to-date they are, so I chose a DIY approach via a &lt;a href="https://nuxt.com/docs/guide/directory-structure/plugins" rel="noopener noreferrer"&gt;Nuxt plugin&lt;/a&gt;. We haven’t covered plugins yet, but they’re an effective way to add new functionality to a base Nuxt app.&lt;/p&gt;

&lt;p&gt;A quick explanatory aside: Nuxt plugin in this context is a file in the &lt;code&gt;/app/plugins&lt;/code&gt; folder that can enrich the Nuxt instance - or even the underlying Vue.js app - via a dedicated function. Nuxt loads these files automatically and runs them at startup.&lt;/p&gt;

&lt;p&gt;In your project you need dependencies on the NPM package &lt;code&gt;bootstrap&lt;/code&gt; and on &lt;code&gt;sass&lt;/code&gt; (similar to &lt;a href="https://dev.to/aloisseckar/nuxt-tutorial-7-adopting-css-1m2a#open-props"&gt;Open Props&lt;/a&gt;, you’ll need some extra CSS processing for your input stylesheet and this is the recommended way). Import Bootstrap styles in the main &lt;code&gt;.scss&lt;/code&gt; file in &lt;code&gt;/app/assets/&lt;/code&gt;:&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="cm"&gt;/* /app/assets/main.scss */&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'bootstrap/scss/bootstrap'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;nuxt.config.ts&lt;/code&gt; we must register this file via &lt;code&gt;css&lt;/code&gt; option:&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;// /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;css&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;~/assets/main.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we also need a plugin that provides interactivity to the Nuxt app via Bootstrap JS. These three lines will do:&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;// /app/plugins/Bootstrap.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;defineNuxtPlugin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;defineNuxtPlugin&lt;/code&gt; function is the handler mentioned above. Its callback receives the Nuxt app instance and lets you interact with it in various ways via its exposed API. For example, using &lt;code&gt;provide&lt;/code&gt; you can &lt;em&gt;provide&lt;/em&gt; everything that the &lt;code&gt;bootstrap&lt;/code&gt; package offers through its exports.&lt;/p&gt;

&lt;p&gt;You can find the source code of a simple example implementation here:&lt;br&gt;
&lt;a href="https://github.com/AloisSeckar/demos-nuxt/tree/main/nuxt-bootstrap" rel="noopener noreferrer"&gt;nuxt-bootstrap @ GitHub&lt;/a&gt; and see it in action.&lt;/p&gt;
&lt;h2&gt;
  
  
  DevExtreme
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://js.devexpress.com/" rel="noopener noreferrer"&gt;DevExtreme&lt;/a&gt; UI library was first introduced to me by an &lt;em&gt;“Angular guy”&lt;/em&gt; colleague. It’s framework agnostic though, so you can use it in Vue as well. The main downside is obvious right away: it’s paid — and not cheap. On the other hand, we used it on a larger real-world project at work, and I have to say building complex nested forms (assembled entirely from scratch) was incredibly smooth with DevExtreme. Their layouts also made it reasonably responsive. The documentation is high quality too, although it took me a bit to learn how to navigate through it.&lt;/p&gt;

&lt;p&gt;Another small blemish is that for some reason &lt;code&gt;Dx&lt;/code&gt; components don’t work with &lt;em&gt;Server-Side Rendering&lt;/em&gt; - or at least, even after several years I never figured out what needed to be done. So you need to set &lt;code&gt;ssr: false&lt;/code&gt; and accept that the app will render via JavaScript only in the client’s browser. That makes it more suitable for SPAs (Single Page Applications), where users treat the website more like a desktop application and are willing to wait a bit for the content to load - which was our case. For projects where loading speed and SEO are critical (typically e-shops), it might not be the best fit.&lt;/p&gt;
&lt;h3&gt;
  
  
  Nuxt integration
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;package.json&lt;/code&gt; you need to reference the base NPM package &lt;code&gt;devextreme&lt;/code&gt; and its Vue port &lt;code&gt;devextreme-vue&lt;/code&gt;. That alone is enough for local development, but the problem is that &lt;em&gt;“tree-shaking”&lt;/em&gt; (removing unused dependencies from the production build) doesn’t recognize you’re using its components, and it happily strips all the definitions out of the final bundle.&lt;/p&gt;

&lt;p&gt;I struggled with this for quite a while and eventually had to &lt;a href="https://github.com/nuxt/nuxt/discussions/16898" rel="noopener noreferrer"&gt;get help&lt;/a&gt;. The key is, again, a Nuxt plugin where you gradually register the DevExtreme components you want to use as Vue components. I strongly recommend registering them under the same names DevExtreme uses — otherwise you lose the ability to easily search their documentation.&lt;/p&gt;

&lt;p&gt;In this example I only use a basic button, but the principle is the same for everything else:&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;// /app/plugins/DevExtreme.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;DxButton&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;devextreme-vue/button&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;defineNuxtPlugin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vueApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DxButton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DxButton&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 &lt;code&gt;DxButton&lt;/code&gt; component then becomes globally available in all your app’s component templates, and you can use it anywhere exactly as described in the DevExtreme documentation without additional constraints.&lt;/p&gt;

&lt;p&gt;You can find the source code of the example implementation here:&lt;br&gt;
&lt;a href="https://github.com/AloisSeckar/demos-nuxt/tree/main/nuxt-dx" rel="noopener noreferrer"&gt;nuxt-dx @ GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After starting the app, you’ll notice a very prominent license warning in the header that DevExtreme injects into the unregistered version. That’s a reminder that without a purchased license, this integration won’t help you much. Still, we’ve demonstrated the general principle of using plugins to add &lt;strong&gt;any&lt;/strong&gt; external Vue components into Nuxt.&lt;/p&gt;
&lt;h2&gt;
  
  
  FontAwesome
&lt;/h2&gt;

&lt;p&gt;After general-purpose UI libraries, let’s move to &lt;a href="https://fontawesome.com/" rel="noopener noreferrer"&gt;FontAwesome&lt;/a&gt;, a popular solution for displaying icons on websites and in apps. It provides a wide selection of vector icons in SVG form (and also as font files) that you can easily use via CSS. FontAwesome allows simple manipulation of icons: changing size, color, and more.&lt;/p&gt;

&lt;p&gt;“FA” used to be an evergreen in web design a few years ago, similar to Bootstrap. I think it’s past its peak too, but let’s still take a look. Since the time I solved the integration manually, a &lt;a href="https://nuxt.com/modules/nuxt-fontawesome" rel="noopener noreferrer"&gt;module&lt;/a&gt; has been created, so it may be easier today than what I describe below. My solution requires at least three new dependencies in &lt;code&gt;package.json&lt;/code&gt; (more if you want to use icons from additional (paid!) sets), and overall it feels a bit clunky to me. But if you’re used to it and can list FontAwesome icon names off the top of your head, you can bring it into Nuxt with you.&lt;/p&gt;

&lt;p&gt;The downside is that, like DevExtreme, FontAwesome doesn’t play nicely with Nuxt SSR, so you need to set &lt;code&gt;ssr: false&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Nuxt integration
&lt;/h3&gt;

&lt;p&gt;I integrated FA into a Nuxt project using a plugin again. This time it’s a bit less intuitive: you first extend the &lt;code&gt;library&lt;/code&gt; object with the icon definitions you want to use, then take the &lt;code&gt;FontAwesomeIcon&lt;/code&gt; object (from another FA package) and register it as a Vue component inside your app:&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;// /app/plugins/FontAwesome.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;faEnvelope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;faFaceSmile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;faHouse&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;@fortawesome/free-solid-svg-icons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;library&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;@fortawesome/fontawesome-svg-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FontAwesomeIcon&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;@fortawesome/vue-fontawesome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// these icons will be available as values for the "icon" attribute&lt;/span&gt;
&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faEnvelope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faFaceSmile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faHouse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// register the "fa" component&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;defineNuxtPlugin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vueApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FontAwesomeIcon&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 component templates you can then use the &lt;code&gt;&amp;lt;fa&amp;gt;&lt;/code&gt; component like this:&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;fa&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;icon=&lt;/span&gt;&lt;span class="s"&gt;"envelope"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"FA 'envelope' icon"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the source code of the example implementation here:&lt;br&gt;
&lt;a href="https://github.com/AloisSeckar/demos-nuxt/tree/main/nuxt-fa" rel="noopener noreferrer"&gt;nuxt-fa @ GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Icônes
&lt;/h2&gt;

&lt;p&gt;Because I don’t consider the solution above particularly good, I’d like to immediately present an alternative. &lt;a href="https://icones.js.org/" rel="noopener noreferrer"&gt;Icônes&lt;/a&gt; is a project focused on providing a large collection of icons for websites and apps. It offers a wide selection of SVG vector icons that are easy to use and customize. Icônes makes icon integration simpler and helps improve the visual quality of user interfaces.&lt;/p&gt;

&lt;p&gt;If you ask me what to use for icons, I’ll recommend &lt;strong&gt;Icônes&lt;/strong&gt; without hesitation. It gives you an easy way to get SVG-based definitions that are portable across environments, requires no new dependencies, and offers a huge library of icon definitions &lt;strong&gt;for free&lt;/strong&gt;. In Nuxt - or rather, directly in Vue - you can introduce each icon as its own component. If you’re not using a UI library that handles icons in its own way (see e.g. the upcoming article about Nuxt UI), this is an excellent lightweight option that lets you use only what you actually need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuxt integration
&lt;/h3&gt;

&lt;p&gt;Unlike previous, this truly works without &lt;strong&gt;any&lt;/strong&gt; external dependency. You simply find the icon you want on the &lt;a href="https://icones.js.org/" rel="noopener noreferrer"&gt;Icônes&lt;/a&gt; website and export the source code directly as a Vue component. In my demo, three selected icons are copied this way into the &lt;code&gt;/components&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;You can find the source code of the example implementation here:&lt;br&gt;
&lt;a href="https://github.com/AloisSeckar/demos-nuxt/tree/main/nuxt-icones" rel="noopener noreferrer"&gt;nuxt-icones @ GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Universal icon component
&lt;/h3&gt;

&lt;p&gt;You may have noticed (as I did) that these generated components look almost identical and differ only in their &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; definitions.&lt;/p&gt;

&lt;p&gt;In another project, I therefore have a &lt;a href="https://github.com/AloisSeckar/ELRHistory/blob/main/app/components/BaseIcon.vue" rel="noopener noreferrer"&gt;component&lt;/a&gt; that renders one of predefined Icônes icons based on a definition from a &lt;a href="https://github.com/AloisSeckar/ELRHistory/blob/main/app/assets/icones.json" rel="noopener noreferrer"&gt;data file&lt;/a&gt;. When the app needs a new icon, a new definition is added into the JSON file and linked from icon component. Feel free to use this idea as inspiration in your own projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this article, we saw four examples of integrating external UI libraries into a Nuxt app. It’s definitely not an exhaustive list of your options, but I think it’s a solid starting point for inspiration.&lt;/p&gt;

&lt;p&gt;In many cases, there’s already a ready-made Nuxt module for popular technologies that handles the integration for you. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/modules/vuetify-nuxt-module" rel="noopener noreferrer"&gt;Vuetify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/modules/primevue" rel="noopener noreferrer"&gt;PrimeVue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/modules/quasar" rel="noopener noreferrer"&gt;Quasar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t have personal experience with any of these, but you’ll surely find support in the active and friendly Nuxt community. And if you have a favorite CSS framework or UI library and you’re missing a Nuxt setup guide, reach me through a comment below and we can try to figure out the integration.&lt;/p&gt;

&lt;p&gt;Next time, we’ll take a closer look at one more option that I intentionally left out so far - and also quickly adopted as my own: the &lt;strong&gt;Nuxt UI module&lt;/strong&gt; directly from the Nuxt team.&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>New in Vue - December 2025</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Mon, 29 Dec 2025 15:14:09 +0000</pubDate>
      <link>https://forem.com/aloisseckar/new-in-vue-december-2025-1hk0</link>
      <guid>https://forem.com/aloisseckar/new-in-vue-december-2025-1hk0</guid>
      <description>&lt;p&gt;Hi, everyone. This will be my 24th and last article in 2025. I wish you all a great start into the upcoming New year 2026 and before it happens, let's do this quick recap of latest development in the &lt;strong&gt;Vue&lt;/strong&gt; / &lt;strong&gt;Nuxt&lt;/strong&gt; / &lt;strong&gt;Vite&lt;/strong&gt; world once again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsywjp8b1lrbu95xywgi.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%2Fcsywjp8b1lrbu95xywgi.png" alt="New Vue newsletter launched" width="66" height="100"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Vue&lt;/strong&gt; will probably end 2025 in maintenance version &lt;strong&gt;3.5.26&lt;/strong&gt;. Vue &lt;strong&gt;v3.5&lt;/strong&gt; was &lt;a href="https://blog.vuejs.org/posts/vue-3-5" rel="noopener noreferrer"&gt;released&lt;/a&gt; more than a year ago, so we can say the fundament of our ecosystem is pretty solid and stable right now. Bugs are being fixed, dependencies patched, and minor tweaks appear from time to time, but overall the code base has been settled and we can develop without worrying we will have to rebuild everything every few months.&lt;/p&gt;

&lt;p&gt;I am not even aware of any ground-breaking changes under development, besides long anticipated &lt;strong&gt;Vapor mode&lt;/strong&gt;, a new strategy for faster components' rendering. This will land in &lt;strong&gt;Vue 3.6&lt;/strong&gt; whose beta release &lt;a href="https://github.com/vuejs/core/releases/tag/v3.6.0-beta.1" rel="noopener noreferrer"&gt;appeared right before Christmas&lt;/a&gt;. If you want to know more, there is &lt;a href="https://vueschool.io/articles/news/vn-talk-evan-you-preview-of-vue-3-6-vapor-mode/" rel="noopener noreferrer"&gt;an article&lt;/a&gt; by Evan You himself. It was released in March though, so you can see the whole process takes its time.&lt;/p&gt;

&lt;p&gt;I would say a solid portion of development effort was re-directed to &lt;strong&gt;Vite&lt;/strong&gt;, the build tool originally designed for &lt;strong&gt;Vue&lt;/strong&gt; which but overgrew into viable cornerstone of JavaScript development. And it is growing further as you can follow at the &lt;a href="https://voidzero.dev/blog" rel="noopener noreferrer"&gt;Void(0) blog page&lt;/a&gt;. &lt;strong&gt;Vite&lt;/strong&gt; itself is now available in version &lt;strong&gt;7.3&lt;/strong&gt; with upcoming &lt;strong&gt;v8&lt;/strong&gt; &lt;a href="https://github.com/vitejs/vite/blob/v8.0.0-beta.5/packages/vite/CHANGELOG.md" rel="noopener noreferrer"&gt;iterating&lt;/a&gt; towards the final release. Everything you need to know about Vite v8, is &lt;a href="https://vite.dev/blog/announcing-vite8-beta" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;Nuxt&lt;/strong&gt; release cycle slowed down a bit in the last couple of weeks. Recent &lt;strong&gt;v4.2&lt;/strong&gt; is here with us for &lt;a href="https://nuxt.com/blog/v4-2" rel="noopener noreferrer"&gt;2 months already&lt;/a&gt; with second patch release &lt;strong&gt;4.2.2&lt;/strong&gt; being the last available version. Former v3 is still being maintained with backported version released together with v4 (&lt;strong&gt;3.20.2&lt;/strong&gt; is the most recent), but the scheduled EOL date (2026-01-31) is &lt;a href="https://github.com/nuxt/nuxt/discussions/33918" rel="noopener noreferrer"&gt;getting close&lt;/a&gt;. The relative stability is the good news, as it means other parts of the ecosystem as various modules may catch up and stabilize themselves around the latest releases too. Meanwhile, new major Nuxt version 5 is somewhere out there in the distance, probably closer than you fear, but farther then you hope. Or maybe vice versa, if you don't want to migrate again (though it is promised it should be easy and straightforward). Anyway, we'll see what happens in 2026.&lt;/p&gt;

&lt;p&gt;Aside vanilla framework, Nuxt offers a number of wonderful modules to extend the core functionality. One of the most prominent official ones is &lt;strong&gt;Nuxt UI&lt;/strong&gt;, the UI toolkit. The library keeps evolving, with latest version &lt;a href="https://github.com/nuxt/ui/releases/tag/v4.3.0" rel="noopener noreferrer"&gt;4.3&lt;/a&gt;, that was released on 17th December. If you pick it for your next project, you may benefit from &lt;a href="https://vueschool.io/articles/vuejs-tutorials/setting-up-your-ide-for-nuxt-ui-a-complete-guide/" rel="noopener noreferrer"&gt;this practical guide&lt;/a&gt; about how to set up your IDE. Actually, you should check the article anyway, because many tips apply to Vue/Nuxt development in general. And I can confirm I was already using most of the extensions mentioned - they're valid and battle-tested.&lt;/p&gt;

&lt;p&gt;If you want to receive ecosystem updates more often than once in a month, there are &lt;a href="https://weekly-vue.news/" rel="noopener noreferrer"&gt;Weekly Vue News&lt;/a&gt;, an email newsletter being delivered each Monday. I certainly recommend you to subscribe, as I am getting a lot of inspirations from there.&lt;/p&gt;

&lt;p&gt;Last but not least I want to spotlight a &lt;a href="https://snyk.io/articles/npm-security-best-practices-shai-hulud-attack/" rel="noopener noreferrer"&gt;comprehensive NPM security guide&lt;/a&gt; composed by cybersecurity expert Liran Tal. In the woke of recent supply-chain attacks outbreak it is always good to remind and revise strategies to keep your projects safer. &lt;/p&gt;

&lt;p&gt;Stay safe, have a great rest of 2025 and a successful start into 2026. See you again in the New year! 🫡&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Upcoming events&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vuejs.amsterdam/" rel="noopener noreferrer"&gt;VueJS Amsterdam 2026&lt;/a&gt; - &lt;strong&gt;12-13 March 2026&lt;/strong&gt;, Amsterdam [NED]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vueconf.us/" rel="noopener noreferrer"&gt;Vueconf.US 2026&lt;/a&gt; - &lt;strong&gt;19-21 May 2026&lt;/strong&gt;, Atlanta [USA]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://madvue.es/" rel="noopener noreferrer"&gt;MadVue 2026&lt;/a&gt; - &lt;strong&gt;22 May 2026&lt;/strong&gt;, Madrid [ESP]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Past issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-november-2025-1l1b"&gt;November 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-october-2025-6i7"&gt;October 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-september-2025-4mef"&gt;September 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-august-2025-lip"&gt;August 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-july-2025-24id"&gt;July 2025&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/aloisseckar"&gt;Follow me&lt;/a&gt; for more updates on Vue, Nuxt, Vite - and maybe even more!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>nuxt</category>
      <category>vite</category>
    </item>
    <item>
      <title>Nuxt Tutorial 7 - Adopting CSS</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sun, 21 Dec 2025 17:46:39 +0000</pubDate>
      <link>https://forem.com/aloisseckar/nuxt-tutorial-7-adopting-css-1m2a</link>
      <guid>https://forem.com/aloisseckar/nuxt-tutorial-7-adopting-css-1m2a</guid>
      <description>&lt;p&gt;We already know how to get a Nuxt application up and running and how to use the various building blocks to assemble it. Now it's time to give our pages a reasonably decent look. In theory, we could handle that ourselves - using CSS styles defined inside the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; sections of individual components. If you're confident with CSS, you can have a nice-looking website pretty quickly.&lt;/p&gt;

&lt;p&gt;Most of the time, though, we don't want to rebuild everything from scratch. In this article you'll first see how easy it is to import ready-made CSS files, and then we'll go through two example integrations with projects that make styling much easier: &lt;code&gt;Tailwind CSS&lt;/code&gt; and &lt;code&gt;Open Props&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import CSS
&lt;/h2&gt;

&lt;p&gt;If you're migrating an older project, you might already have your &lt;code&gt;.css&lt;/code&gt; style files ready. Or you may be using a service that ships its styles as a standalone &lt;code&gt;.css&lt;/code&gt; file (i.e. when &lt;a href="https://getbootstrap.com/docs/5.3/getting-started/download/#cdn-via-jsdelivr" rel="noopener noreferrer"&gt;using Bootstrap from a CDN&lt;/a&gt;). Nuxt offers a very simple integration for these cases.&lt;/p&gt;

&lt;p&gt;Upload your style files into the &lt;code&gt;/app/assets&lt;/code&gt; folder (usually into &lt;code&gt;/css&lt;/code&gt; sub-directory for better clarity). Then reference them via the &lt;code&gt;css&lt;/code&gt; option in your &lt;code&gt;nuxt.config.ts&lt;/code&gt; config file, i.e.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="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;css&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;~/assets/css/main.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The imported styles are then automatically available across the whole application.&lt;/p&gt;

&lt;p&gt;If needed, you can also load them individually per component - either in the &lt;code&gt;&amp;lt;script setup&amp;gt;&lt;/code&gt; section:&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;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/assets/css/main.css&lt;/span&gt;&lt;span class="dl"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or inside &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;:&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;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&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="sx"&gt;url("~/assets/css/main.css")&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;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Little sidenote: In example above I added &lt;code&gt;scoped&lt;/code&gt; attribute to denote the style sheet should only be relevant for the component file itself. Otherwise the styles would leak globally. While this may be a bad thing, you can also do it on purpose (preferably in &lt;code&gt;app.vue&lt;/code&gt;) and make your styles available throughout the whole application. More common is to use the &lt;code&gt;css&lt;/code&gt; option in Nuxt config though.&lt;/p&gt;

&lt;p&gt;Even with just this, you can achieve quite a lot. For examples of integrations with more advanced CSS libraries, keep reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; is a CSS framework for styling websites and applications. Unlike the traditional CSS approach, you don't write your own classes; instead, you compose the final look from a large set of predefined utility classes, which enables fast and flexible styling and layouts. Tailwind itself is also highly adaptable and configurable - for example when it comes to defining color schemes.&lt;/p&gt;

&lt;p&gt;It deserves an article of its own (which I originally planned, and maybe I'll get to it eventually). For me it's currently the number one choice for building a website's look. It requires a slightly different philosophy, but I adjusted in a day, and I don't want to do it any other way now. I migrated couple of my personal projects to Tailwind CSS already, where the original design was done “by hand”. Converting them into the corresponding Tailwind structure usually took just one evening.&lt;/p&gt;

&lt;p&gt;It's fair to note it's not a CSS framework “for purists” because it does impose some constraints. Others criticize it for creating too much abstraction and giving users the illusion that they understand what they're doing - until they hit a trap they can't resolve because they don't really know the underlying CSS. My experience, however, is that it will be enough in vast majority of situations, and its efficiency gives you more time to handle the remaining edge cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuxt integration
&lt;/h3&gt;

&lt;p&gt;Getting started with Tailwind CSS is now very straightforward. The creators ship a plugin for Vite (the build tool Nuxt uses), which you just install together with the main library:&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;tailwindcss @tailwindcss/vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the plugin to &lt;code&gt;nuxt.config.ts&lt;/code&gt; along with a reference to the CSS file that imports Tailwind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tailwindcss&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;@tailwindcss/vite&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="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Vite integration plugin&lt;/span&gt;
  &lt;span class="na"&gt;vite&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;tailwindcss&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;// CSS file importing Tailwind classes&lt;/span&gt;
  &lt;span class="na"&gt;css&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;~/assets/css/tailwind.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

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

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step is creating a simple CSS file that you load via the &lt;code&gt;css&lt;/code&gt; option. Its content can be as minimal as:&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;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo app
&lt;/h3&gt;

&lt;p&gt;You can try the approach above in practice. The source code for the demo implementation is here: &lt;a href="https://github.com/AloisSeckar/demos-nuxt/tree/main/nuxt-tailwind" rel="noopener noreferrer"&gt;nuxt-tailwind @ GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo focuses on both integration and usage. All the visual behavior is in &lt;code&gt;/app/app.vue&lt;/code&gt;. It contains several examples of how Tailwind CSS can be applied. It also shows that utility classes can be freely combined with classic “plain” CSS. In &lt;code&gt;/app/assets/css/tailwind.css&lt;/code&gt; you can see how easily you can extend Tailwind CSS' default &lt;code&gt;@theme&lt;/code&gt; with your own colors or fonts if the &lt;a href="https://tailwindcss.com/docs/colors" rel="noopener noreferrer"&gt;base palette&lt;/a&gt; isn't enough.&lt;/p&gt;

&lt;p&gt;Based on a personal bad experience, I also decided to add one cautionary example to the demo alongside the positive ones. I'm risking that it might inspire someone to use it and fall into the same trap I once did, but hopefully it will save many of you from unnecessary problems. What is it about? The Tailwind CSS authors encourage you to use utility classes directly in HTML templates as attributes on individual elements whenever possible. Not everyone (hi, former me) likes that, because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Class lists can get really long&lt;/li&gt;
&lt;li&gt;The same definitions repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the same time, a beginner - armed with general programming truths and ready to “fix” things - will sooner or later discover the &lt;a href="https://tailwindcss.com/docs/functions-and-directives#apply-directive" rel="noopener noreferrer"&gt;&lt;code&gt;@apply&lt;/code&gt; directive&lt;/a&gt;, which &lt;em&gt;seems&lt;/em&gt; to allow defining reusable classes in CSS files. And to be fair: it does allow it, and it works perfectly. The problem is that it severely obfuscates the implementation. After a few months, you forget you styled a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; globally and wonder why your inline definitions don't apply. The demo app includes a clear example with the color of an HTML link at the bottom of the page. I wrote more about this in &lt;a href="https://dev.to/aloisseckar/i-was-using-tailwind-wrong-so-you-dont-have-to-4h7j"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;End of the diversion. Definitely take a look at Tailwind CSS - you might end up liking it like I do, along with thousands of other developers who prioritize speed and efficiency over perfect control via native CSS.&lt;/p&gt;

&lt;p&gt;P.S.: Previously I handled the integration via a dedicated &lt;a href="https://tailwindcss.nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt module&lt;/a&gt;, which was a bit easier to set up (install + add the module). I'm not sure about its current status, though. After &lt;strong&gt;Tailwind v4&lt;/strong&gt; was released, it stopped working for me. Its new version has been stuck in beta for months, and it almost looks like its historical role is over. If the situation changes, I'll come back and update this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Props
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://open-props.style/" rel="noopener noreferrer"&gt;Open Props&lt;/a&gt; is based on a similar idea to Tailwind CSS - it provides a set of prebuilt styles that may constrain you a bit, but in return you don't have to reinvent the wheel everywhere. Instead of shipping complete prebuilt CSS classes, Open Props encodes the styling information as &lt;a href="https://www.w3schools.com/css/css3_variables.asp" rel="noopener noreferrer"&gt;CSS variables&lt;/a&gt;, which you can use and compose inside your own classes. To be honest: Tailwind v4 also uses CSS variables, and you may have already tasted them (even unknowingly) - see how the demo app extends the default &lt;code&gt;@theme&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open Props sits one level lower. It doesn't abstract away writing CSS entirely; it “only” provides ready-made, proven values. That gives you more freedom, but it's a bit more work. The main advantage is how lightweight and scalable it is - the library lets you &lt;a href="https://open-props.style/#overview" rel="noopener noreferrer"&gt;use only the small subset&lt;/a&gt; you actually need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuxt integration
&lt;/h3&gt;

&lt;p&gt;Because Nuxt (Vue) can't replace CSS variables with actual values at build time by itself, you need to add a CSS processor (&lt;code&gt;postcss&lt;/code&gt; in my demo) along with a plugin that performs the substitution after being fed the definitions from &lt;code&gt;OpenProps&lt;/code&gt;. That's about the only catch; otherwise you just import the &lt;code&gt;open-props&lt;/code&gt; NPM package and start building CSS definitions. There is a showcase of a few styles in the demo.&lt;/p&gt;

&lt;p&gt;What I like most about Open Props are the ready-made &lt;a href="https://open-props.style/#gradients" rel="noopener noreferrer"&gt;gradients&lt;/a&gt; - smooth transitions between colors. Sure, it's not &lt;em&gt;that&lt;/em&gt; hard to write them yourself in plain CSS (I even watched a demo of building a “gradient editor” recently), but if you're not a web designer at heart, who wants to grind through that?&lt;/p&gt;

&lt;p&gt;The source code for the demo implementation is here:&lt;br&gt;
&lt;a href="https://github.com/AloisSeckar/demos-nuxt/tree/main/nuxt-openprops" rel="noopener noreferrer"&gt;nuxt-openprops @ GitHub&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We've shown how to load internal and external CSS files into a Nuxt app, plus two examples of CSS libraries and how to integrate them. There are, of course, many other options. If you have a favorite UI/styling library, check whether it already has a Nuxt integration (most commonly in the form of a &lt;a href="https://nuxt.com/modules" rel="noopener noreferrer"&gt;module&lt;/a&gt;). If not, it may be enough just to load the resulting CSS files the way we described at the start and use them directly. And if you get stuck, reach out and we can try to figure something out together.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/aloisseckar/nuxt-tutorial-7-ui-integrations-4mjm"&gt;upcoming tutorial article&lt;/a&gt;, we'll move from general styling to more advanced &lt;strong&gt;UI libraries&lt;/strong&gt;, which provide not only styles but also prebuilt UI components. We'll look at a few of them along with examples of integrating them into Nuxt.&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I made my Vitest suite in Nuxt run ten times faster</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 20 Dec 2025 11:21:08 +0000</pubDate>
      <link>https://forem.com/aloisseckar/i-made-my-vitest-suite-in-nuxt-run-ten-times-faster-6j7</link>
      <guid>https://forem.com/aloisseckar/i-made-my-vitest-suite-in-nuxt-run-ten-times-faster-6j7</guid>
      <description>&lt;p&gt;Previously I had to wait &lt;strong&gt;about 3 minutes&lt;/strong&gt; till my tests finish, now the same suite clocks &lt;strong&gt;under 20 seconds&lt;/strong&gt;. Let's see what the bottleneck was and how I &lt;em&gt;vibe coded&lt;/em&gt; the re-design. &lt;strong&gt;Spoiler:&lt;/strong&gt; It was painful running in circles and despite AI gave me some valuable insights, I had to be the Deus ex Machina (pun intended).&lt;/p&gt;

&lt;p&gt;I guess I don't have to explain the importance of writing automated tests to protect applications from introducing unnecessary errors. It is never 100%, but as the time flows, test suites grow larger, cover more situations and especially prevent bugs from &lt;strong&gt;re-appearing&lt;/strong&gt;. I would say it is rather hard to develop a habit of writing tests regularly, especially in personal projects, but current tooling focus on making it easy. Setting up &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt; to &lt;a href="https://nuxt.com/docs/4.x/getting-started/testing" rel="noopener noreferrer"&gt;test Nuxt applications&lt;/a&gt; is quick and you're good to go. &lt;/p&gt;

&lt;p&gt;You can develop a new feature, write new tests for it, and run the suite to check if:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;New tests are passing - so the new feature works as intended&lt;/li&gt;
&lt;li&gt;Old tests are &lt;strong&gt;still passing&lt;/strong&gt; - so you didn't accidentally break any seemingly unrelated things&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then you can commit with greater confidence.&lt;/p&gt;

&lt;p&gt;But there is a catch - more tests run longer and former seconds may prolong to minutes. So you wait minutes and then you come back and see the test suite failed. You fix the error, run the tests again...and wait minutes again. Do this couple of times and you suddenly don't like the whole idea that much.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framing the problem
&lt;/h2&gt;

&lt;p&gt;Test suite for my Nuxt module for &lt;a href="https://neon.com/" rel="noopener noreferrer"&gt;Neon database&lt;/a&gt; connection reached 180+ seconds of runtime. This didn't feel right anymore. The tests were testing, but the inefficiency hit my DevEx hard. It was the turning point when &lt;em&gt;premature optimization&lt;/em&gt; turned into a required one.&lt;/p&gt;

&lt;p&gt;Because it is 2025, I invited AI to check the situation and brainstorm for possible solutions. &lt;/p&gt;

&lt;p&gt;Copilot quickly pointed out, that I am using separate Nuxt app instance for each test file. At first it looked like a good decision to separate concerns - testing SELECT operations in an app for selects, INSERT in another one. It should help keeping things small and organized. But booting them up takes time and all tests had to wait until their app is rendered.&lt;/p&gt;

&lt;p&gt;To make the problem worse, I decided to run my tests sequentially. Because if something messes up and the module cannot even connect to the database, why tests the functions? Or if SELECTs are failing due to an error in constructing the SQL query, why to try INSERTs that would also inevitably fail? This stacks the waiting times up. And despite everything eventually works, I ended up waiting those 3 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  First iteration - double speed
&lt;/h2&gt;

&lt;p&gt;The fix was apparent - merge the separate test apps into one. This was quite easy to do, because I designed my tests so that each has its dedicated Nuxt page (this means separate URL) where it connects, perform SQL action bound to a button and check the resulting HTML for expected values. So I really just have to squash multiple &lt;code&gt;/app/pages&lt;/code&gt; directories into one and make some small adjustments in &lt;code&gt;app.vue&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;But it wasn't enough. Because my E2E test files would still&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;rootDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./neon-test-app&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;via a function from &lt;code&gt;@nuxt/test-utils/e2e&lt;/code&gt; package. This would still create and bind new instance of Nuxt app for &lt;strong&gt;each&lt;/strong&gt; test file. So it would still take a lot of time.&lt;/p&gt;

&lt;p&gt;To be honest, there was already an observable speed-up. I guess it was because it can re-use some cached pieces and mounting the same app 5 times was about twice as fast as mounting 5 different apps, but it was still very sub-optimal.&lt;/p&gt;

&lt;p&gt;What I really needed was that whenever any test case does&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;createPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it should connect to the &lt;strong&gt;same&lt;/strong&gt; instance of underlying demo application. This would only start once and then all tests should call it with a snap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second iteration - 10x speed, but...
&lt;/h2&gt;

&lt;p&gt;So how to do that? I didn't know. So I asked my smarter/dumber electronic assistant. It came up with very unorthodox solution. If you want to follow along, the result was committed &lt;a href="https://github.com/AloisSeckar/nuxt-neon/commit/32bc4150adefaab175d56ffb2b4227bb505776d6" rel="noopener noreferrer"&gt;HERE&lt;/a&gt;. I will now sum up the most important pieces.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;vitest.config.ts&lt;/code&gt;, new setting was added:&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="nx"&gt;globalSetup&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;./node_modules/@nuxt/test-utils/dist/runtime/global-setup.mjs&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;It took me a while to understand what it is even intended to do. I challenged my Copilot's reasoning and cross-checked with my standalone ChatGPT 5.2 Plus. It confirmed the idea. Despite it testified it didn't find any article or discussion about it, it says it &lt;em&gt;assumed&lt;/em&gt; from the code, it would work. The justification made sense. I had nothing to lose. So I believed.&lt;/p&gt;

&lt;p&gt;In short, running this file in &lt;code&gt;globalSetup&lt;/code&gt; before other Vitest actions start should "magically" grant me existence of a mounted app in emulated browser. To tell it, which app I want to mount, I should feed &lt;code&gt;NUXT_TEST_OPTIONS&lt;/code&gt; environment variables which are swallowed during the process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fileURLToPath&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test/neon-test-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Used by @nuxt/test-utils/runtime/global-setup&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;NUXT_TEST_OPTIONS&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// path to neon-test-app&lt;/span&gt;
  &lt;span class="nx"&gt;rootDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// don't create a Playwright browser in globalSetup&lt;/span&gt;
  &lt;span class="na"&gt;browser&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second integral part was &lt;code&gt;e2e.setup.ts&lt;/code&gt; - a file to add actions to be run by Vitest before every test file. In &lt;code&gt;beforeAll&lt;/code&gt; method it should construct a virtual browser instance - but only for the first time when it is non-existent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTestContext&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createBrowser&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;And just like that, this would grant all test files access to the shared context with prepared virtual browser with mounted Nuxt test app. And all tests should just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;createPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and start navigating to desired routes and testing the stuff.&lt;/p&gt;

&lt;p&gt;I tried to follow my usual sceptical approach to AI and understand the intents behind the code spitted out - at least briefly. I couldn't grasp all details, but overall it appeared to make sense. And when I tried it - &lt;strong&gt;it worked&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Well, not for the first time. We had to tweak couple of things, but in the end the suite was running. And boy, it was fast! Less than 20 seconds and all tests already passed. If I deliberately made a test fail to check, whether it actually runs and tests something, it started failing just as expected. My job here was done. Commit, close the issue, go to sleep with good feeling.&lt;/p&gt;

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

&lt;p&gt;Except there was a "tiny" detail I missed at the time. The Vitest suite was running fine, but once the process is finished, the output just disappeared from the terminal! So there was no chance to recover the result. Unless you looked at it, you had no chance to tell whether it passed or not. And if it failed, there was no way to see what exactly went wrong.&lt;/p&gt;

&lt;p&gt;My tests essentially become useless 😨&lt;/p&gt;

&lt;h2&gt;
  
  
  Third iteration - to hell and back
&lt;/h2&gt;

&lt;p&gt;The reason why I missed it when I worked on it is interesting per se. &lt;/p&gt;

&lt;p&gt;Because we tried a number of things with my Copilot during the process, at some time we introduced &lt;code&gt;$env:DEBUG='@nuxt/test-utils*'&lt;/code&gt; setting into my current terminal. Thanks to this extra environment setting, the program started to log differently - extensively and without interruption. All the log messages were prefixed with &lt;code&gt;[source]&lt;/code&gt; tags, but I barely noticed that. I saw the Vitest output and I was happy.&lt;/p&gt;

&lt;p&gt;But the default way of things with the &lt;code&gt;globalSetup&lt;/code&gt; is, that the terminal is first occupied with the test Nuxt app build and then it is automatically switched into a new &lt;em&gt;alternate screen&lt;/em&gt; that is visible in the terminal, but only as long as the associated process is running. Then it is just thrown away and replaced by former (empty) terminal.&lt;/p&gt;

&lt;p&gt;I didn't know that, until I spend &lt;strong&gt;HOURS&lt;/strong&gt; of useless attempts of adding config here and there. AI was spitting out fabricated theories, and I was struggling with implementing them and keep failing again and again.&lt;/p&gt;

&lt;p&gt;After some time we at least isolate the &lt;code&gt;DEBUG&lt;/code&gt; option as a possible workaround. But it also came with a very noisy and verbose output. Filtering out was possible, but platform-dependant. Or I could have wait for 20 seconds of silence and then got the filtered result processed by Node. Also not good. &lt;/p&gt;

&lt;p&gt;I tried to bargain with AI until I finally - for the first time I thing - managed to force ChatGPT into saying: &lt;em&gt;"No, this is not possible."&lt;/em&gt; More precisely, it wrote: &lt;em&gt;"This is the moment where I need to be very explicit and honest, because you’ve now hit a hard boundary, not a missing trick."&lt;/em&gt; which I find hilarious now, but I didn't laugh back then.&lt;/p&gt;

&lt;p&gt;The harsh truth hitting me that evening. I wasted hours chasing my own shadow. With AI happily assisting me and encouraging me to continue.&lt;/p&gt;

&lt;p&gt;I have fast, but badly observable tests. Either no output at all, or an output littered by tons of non-related debug messages, or platform-dependant script commands to polish it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fourth iteration - deus ex homine
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aloisseckar/the-mysterious-case-of-the-query-that-never-ran-1gd9"&gt;my last article&lt;/a&gt; I went to sleep on the problem, and it didn't help. But this time it does. When I woke up this morning, prepared to finalize the ugly but so-so working solution, I suddenly got an out-of-the-box idea.&lt;/p&gt;

&lt;p&gt;What if I stop trying to abuse Vitest into doing something it is not designed for?&lt;/p&gt;

&lt;p&gt;What if I just aggregate my test files into one big suite?&lt;/p&gt;

&lt;p&gt;Like that it would only need to build and mount the test app once and then it will run fast. Yes, there would be a little trade-off - one big test file with a lot of test cases. But to be honest, this doesn't really matter from the runtime perspective. And I can still keep the source code isolated by having separate definition files that will be dynamically imported in the actual single &lt;code&gt;e2e.test.ts&lt;/code&gt; file, the only one that will be executed by Vitest.&lt;/p&gt;

&lt;p&gt;And so just like that, I throw away most of the "clever hacks" from the solution #3 and came up with something else. Despite my AI assistants failed yesterday, I indeed used them again to help me fabricated the vivid solution. Now when I was back with a clear intention in mind, I got helpful output and tips again. The final solution is &lt;a href="https://github.com/AloisSeckar/nuxt-neon/tree/9a7d9745f28178708c2fa7222b76ad6573842d22" rel="noopener noreferrer"&gt;HERE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Calls for &lt;code&gt;globalSetup&lt;/code&gt; and even the &lt;code&gt;setupFiles&lt;/code&gt; were removed from &lt;code&gt;vitest.config.ts&lt;/code&gt;. The all I need now is a nice, small and clean &lt;code&gt;e2e.test.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fileURLToPath&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;node:url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setup&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;@nuxt/test-utils/e2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// only setup nuxt-test-app ONCE&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;rootDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../neon-test-app&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="c1"&gt;// Playwright browser is not required for now&lt;/span&gt;
  &lt;span class="na"&gt;browser&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="c1"&gt;// import and run E2E test suites AFTER the test app is ready&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../neon-test-suites/01-basic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../neon-test-suites/02-select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../neon-test-suites/03-insert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../neon-test-suites/04-update&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../neon-test-suites/05-delete&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;It prepares the Nuxt test app via dedicated &lt;code&gt;setup&lt;/code&gt; function and then just awaits and executes one file with test definitions after another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that's it.&lt;/strong&gt; No hacks, no mess in console. It works like charm and it is still fast. 20 seconds and you're done. The output remains in the console.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  To infinity and beyond
&lt;/h2&gt;

&lt;p&gt;So now you know how I wrestle with Vitest with Nuxt and won. If you'd like to know more details about the final implementation or you have objections and ideas for improvement, let me know in the comments.&lt;/p&gt;

&lt;p&gt;You also seen how AI tools still lead you astray easily. To their defence, in this case both Copilot and ChatGPT pretty much resembled a common developer, who is desperately throwing solutions that "should work" without realizing the whole picture. Where they failed and I eventually succeeded was ability to step back and re-think the whole situation. I believe this is still the gap between human developers and the artificial pseudo-intelligence.&lt;/p&gt;

&lt;p&gt;Lastly, this wasn't meant to be an anti-AI rant. It is helping me on daily basis, and I really enjoy getting further and further with its assistance. I just think there are still limits we need to be aware of. It was another nice lesson for me, and I hope you found it somewhat interesting too. &lt;/p&gt;

&lt;p&gt;Looking forward to your feedback and questions.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nuxt</category>
      <category>vitest</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Mysterious Case of the Query that Never Ran</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Sat, 13 Dec 2025 09:54:47 +0000</pubDate>
      <link>https://forem.com/aloisseckar/the-mysterious-case-of-the-query-that-never-ran-1gd9</link>
      <guid>https://forem.com/aloisseckar/the-mysterious-case-of-the-query-that-never-ran-1gd9</guid>
      <description>&lt;p&gt;Over the past weekend I encountered a weird bug.&lt;/p&gt;

&lt;p&gt;I was refactoring code for my personal website. I am a casual runner and I have a &lt;a href="https://alois-seckar.cz/run" rel="noopener noreferrer"&gt;page with all my run records&lt;/a&gt;. The data for it are stored in &lt;a href="https://neon.com/" rel="noopener noreferrer"&gt;Neon database&lt;/a&gt; and I am reading them through my own custom &lt;a href="https://www.npmjs.com/package/nuxt-neon" rel="noopener noreferrer"&gt;nuxt-neon&lt;/a&gt; module.&lt;/p&gt;

&lt;p&gt;I was updating the code to reflect the latest changes in my module. Namely I tried to change the filtering by date (year and/or month). Originally, I use literal string &lt;code&gt;WHERE&lt;/code&gt; condition &lt;code&gt;r.date BETWEEN '${fromDate}' AND '${toDate}'&lt;/code&gt;. Now I planned to battle-test my new object syntax:&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="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;r.date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fromDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;r.date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AND&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;Now, you may argue this is much more complicated and you'd probably be right. But I wanted to test how it works beyond sterile tests defined inside my module. And...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It was a complete failure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No data. Just an ugly HTTP error when trying to get data from the Nuxt server endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NuxtNeonClientError in fetchFromNeonBackend: 
[POST] "/api/_neon/select": 400 Bad Request (status: 500). 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make a quick explanation - my module is providing a client-side &lt;a href="https://vuejs.org/guide/reusability/composables.html#what-is-a-composable" rel="noopener noreferrer"&gt;composable&lt;/a&gt; &lt;code&gt;useNeon&lt;/code&gt; that exposes couple of wrappers for SQL functions (namely &lt;code&gt;select&lt;/code&gt; in this case). Those wrappers are essentially just forwarding input query to Nuxt server endpoints for each SQL function. Like this, I can keep the DB credentials safely stored on the server side and not leak them via runtime config into the browser. AFAIK Neon does not provide safe public API keys as services like Supabase do.&lt;/p&gt;

&lt;p&gt;So I started to investigate what might be wrong.&lt;/p&gt;

&lt;p&gt;At first, I thought I messed up the implementation, and I am constructing a bad SQL query. But there were two "buts" to this theory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The very same query worked just fine when I reproduced it as a test case in my module codebase. And in fact, I was already testing whether &lt;em&gt;"greater than / less than"&lt;/em&gt; operators pick the right data.&lt;/li&gt;
&lt;li&gt;Despite I set enhanced debug logs for my module (you can debug both the execution flow and the actual SQL produced inside the module), I saw no data for my request. Actually, it looked like the API endpoint wasn't even called.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I found myself desperately tampering with the files inside my &lt;code&gt;node_modules&lt;/code&gt; folder trying to track down where I am and why I am not seeing any usable output to understand the issue.&lt;/p&gt;

&lt;p&gt;I had a faint suspicion that the literal characters &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;&lt;/code&gt; should be the root issue. But no viable theory of why it would matter.&lt;/p&gt;

&lt;p&gt;I gave it a one-day break and unsurprisingly I didn't get any brilliant idea overnight. As a last resort, I just tried to ask my Copilot. I was postponing this move, because &lt;em&gt;&lt;strong&gt;what would my question be&lt;/strong&gt;&lt;/em&gt;? I don't understand what is going on, how should I instruct mindless LLM to come up with a solution? Without good prompt, I can't get good result, can I?&lt;/p&gt;

&lt;p&gt;Well no, but actually yes. Copilot didn't magically save the day. But it gave me one important clue to follow: &lt;em&gt;"Reproduce with curl (bypasses browser and frontend)"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This means, just call the API endpoint directly with a bare HTTP request. Strip it down from all frontend framework noise. Then you might understand better what is breaking up before backend can process your request. So I did as machine mind said and &lt;em&gt;voilà!&lt;/em&gt;, everything clicked together.&lt;/p&gt;

&lt;p&gt;Turned out my request was being rejected by &lt;a href="https://nuxt.com/modules/security" rel="noopener noreferrer"&gt;Nuxt Security module&lt;/a&gt; and its XSS attack protection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...\node_modules\.pnpm\nuxt-security@2.4.0_magicast@0.3.5_rollup@4.52.5\
node_modules\nuxt-security\dist\runtime\server\middleware\
xssValidator.js:38:18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like that, it is pretty obvious. The problem was this particular line was swallowed inside Nuxt leaving me only with a cryptic error with no apparent reason.&lt;/p&gt;

&lt;p&gt;Naturally, it was because of the &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;&lt;/code&gt; brackets. Nuxt Security treats them as potentially malicious attempt to smuggle a script into the payload. Between me and you - this module is pretty good in protecting your Nuxt applications with little to no effort of setting it up, but then it keeps biting you back like that.&lt;/p&gt;

&lt;p&gt;So this was the missing piece. It worked in my tests because the naive demo app is not concerned about security that much. But I chose to shield my homepage with more advanced protection, and it backfired.&lt;/p&gt;

&lt;p&gt;To mitigate the problem, I am adding an internal mapping that translates angle brackets to text abbreviations on client and back when constructing the actual SQL query on the server. For user convenience I decided to keep them available instead of forcing everyone to learn they should use &lt;code&gt;GT&lt;/code&gt; and &lt;code&gt;LT&lt;/code&gt; abstraction.&lt;/p&gt;

&lt;p&gt;Despite the root cause and the solution itself isn't groundbreaking, I still thought the unique combination of circumstances is worth an article. I wanted to share following lessons learned from this debugging struggle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't be shy to ask AI for help&lt;/strong&gt; - Even if you don't know the right question yet, just ask: &lt;em&gt;"Why do I have this error?"&lt;/em&gt; Start explaining the situation and you may &lt;em&gt;vibedebug&lt;/em&gt; the solution eventually. It keeps surprising me how effective this becomes lately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolate the problem&lt;/strong&gt; - If backend seems not to respond properly, call it directly to abstract from possible interference. I should have known this after all those years in software development, yet I got lost in the frontend land and needed virtual assistant to pull me out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Take 3rd party SW in account&lt;/strong&gt; - Maybe it is not just your code that causes the trouble. Maybe some other library is acting up (because of something in your code).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have been developing software for at least two decades now. I feel like a senior and being treated as such. But as you can see, I keep getting stuck in trivial situations and feeling rather stupid afterwards. I believe this is part of the process. You keep running into the walls, keep falling down and keep raising up, being ready for more.&lt;/p&gt;

&lt;p&gt;Thank you for reading this and may it help someone in the future. Until the next awkward issue to solve.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nuxt</category>
      <category>debugging</category>
      <category>learning</category>
    </item>
    <item>
      <title>New in Vue - November 2025</title>
      <dc:creator>Alois Sečkár</dc:creator>
      <pubDate>Thu, 27 Nov 2025 13:21:49 +0000</pubDate>
      <link>https://forem.com/aloisseckar/new-in-vue-november-2025-1l1b</link>
      <guid>https://forem.com/aloisseckar/new-in-vue-november-2025-1l1b</guid>
      <description>&lt;p&gt;Attention! More news and events from the ever-evolving world of &lt;strong&gt;Vue&lt;/strong&gt;, &lt;strong&gt;Nuxt&lt;/strong&gt;, &lt;strong&gt;Vite&lt;/strong&gt;, and their awesome open-source ecosystems are there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsywjp8b1lrbu95xywgi.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%2Fcsywjp8b1lrbu95xywgi.png" alt="New Vue newsletter launched"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I would like to start 5th issue of my Vue newsletter with something I haven't done yet - sharing a video:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/ofQV3xiBgT8"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;As you can see, it is a flashback from the &lt;a href="https://viteconf.amsterdam/" rel="noopener noreferrer"&gt;ViteConf&lt;/a&gt; I was talking about last month. The reason why am I featuring this is interesting topic of &lt;em&gt;not-so-distant&lt;/em&gt; future of our ecosystem together with the awesome stage presence of the speaker. I remember my very first impression was &lt;em&gt;"Oh wow, the guy sounds quite nervous"&lt;/em&gt;. But then Jim Dummet delivered one of the most memorable IT talk I can think about. The fact it was his only second live talk ever makes it even more awesome. Give it a try if you have a spare 30 minutes.&lt;/p&gt;

&lt;p&gt;Speaking about ViteConf, two more interesting news I forgot to point out in October were released there. The &lt;a href="https://github.com/vitejs/devtools" rel="noopener noreferrer"&gt;Vite DevTools&lt;/a&gt; project was announced by Anthony Fu, which will bring the developer experience one more step (or even a leap) forward. Despite it seems nowhere near close to stable release yet, it is something worth paying attention to. And I am looking forward to the result. Second update came from &lt;a href="https://nitro.build/" rel="noopener noreferrer"&gt;Nitro&lt;/a&gt; - an alpha version of its v3 major upgrade was released during the conference by its maintainer Pooya Parsa. Also not finished yet, but another gamechanger in the ecosystem that is knocking at the door.&lt;/p&gt;

&lt;p&gt;To get a comprehensive overview of what happened in Amsterdam (and &lt;strong&gt;should NOT&lt;/strong&gt; stay in Amsterdam) was &lt;a href="https://voidzero.dev/posts/whats-new-viteconf-2025" rel="noopener noreferrer"&gt;written by Alex Lichter&lt;/a&gt;, so if you want more detail plus direct links to respective talks, give it a go.&lt;/p&gt;

&lt;p&gt;Overall November was quieter in terms of hot ecosystem news. Or they went past me if there were any. The one that hit me and I find it worth mentioning was the alpha release of &lt;a href="https://github.com/nuxt/hints" rel="noopener noreferrer"&gt;Nuxt Hints&lt;/a&gt; module. It is promised to provide &lt;em&gt;"real-time feedback on your application's performance, accessibility, and security right in your browser"&lt;/em&gt;. I believe it will become very helpful to make our Nuxt apps better. It is out for two weeks now, and I am looking forward to set it up and give it a try. Sadly, there wasn't time for that yet.&lt;/p&gt;

&lt;p&gt;Tiny promo (getting no commission for this, I just like it) - if you need an easy way of integrating &lt;code&gt;Share with &amp;lt;&amp;lt;insert_socal_network&amp;gt;&amp;gt;&lt;/code&gt; buttons to your website, &lt;a href="https://nuxt-social-share.stefanobartoletti.it/" rel="noopener noreferrer"&gt;Nuxt Social Share&lt;/a&gt; is there for help. It is incredibly easy with zero knowledge of the APIs required - you just add a component, pick the target via a string prop and get the sharing functionality. I set it up within minutes for my blog. The "hardest" part is to adjust the default styling, but it uses Tailwind CSS, so it is actually not that hard at all. I really recommend this 👍&lt;/p&gt;

&lt;p&gt;Did you ever feel overwhelmed by different approaches to managing stateful data in Vue applications? Michael Hoffmann wrote a useful guide for &lt;a href="https://mokkapps.de/blog/vue-state-management-composables-provide-inject-pinia" rel="noopener noreferrer"&gt;how to pick between Composables, Provide/Inject, and Pinia&lt;/a&gt; which might help you navigate through it. He also runs &lt;a href="https://weekly-vue.news/" rel="noopener noreferrer"&gt;Weekly Vue News&lt;/a&gt; email newsletter, so consider subscribing for even more "New In Vue" stuff from him.&lt;/p&gt;

&lt;p&gt;I'll be back in late December. Thank you for reading this and if you have any questions or feedback, feel free to use the comments bellow.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Upcoming events&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vuejs.amsterdam/" rel="noopener noreferrer"&gt;VueJS Amsterdam 2026&lt;/a&gt; - &lt;strong&gt;12-13 March 2026&lt;/strong&gt;, Amsterdam [NED]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vueconf.us/" rel="noopener noreferrer"&gt;Vueconf.US 2026&lt;/a&gt; - &lt;strong&gt;19-21 May 2026&lt;/strong&gt;, Atlanta [USA]&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://madvue.es/" rel="noopener noreferrer"&gt;MadVue 2026&lt;/a&gt; - &lt;strong&gt;22 May 2026&lt;/strong&gt;, Madrid [ESP]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Past issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-october-2025-6i7"&gt;October 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-september-2025-4mef"&gt;September 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-august-2025-lip"&gt;August 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aloisseckar/new-in-vue-july-2025-24id"&gt;July 2025&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/aloisseckar"&gt;Follow me&lt;/a&gt; for more updates on Vue, Nuxt, Vite - and maybe even more!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>nuxt</category>
      <category>vite</category>
    </item>
  </channel>
</rss>
