<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vanessa Otto</title>
    <description>The latest articles on DEV Community by Vanessa Otto (@vannsl).</description>
    <link>https://dev.to/vannsl</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%2F224736%2F62234d19-554b-4e8f-91db-413d4fcb35a2.jpg</url>
      <title>DEV Community: Vanessa Otto</title>
      <link>https://dev.to/vannsl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vannsl"/>
    <language>en</language>
    <item>
      <title>The Surprisingly Weird World of Favicons (And How to Survive It)</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Mon, 14 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/vannsl/the-surprisingly-weird-world-of-favicons-and-how-to-survive-it-5a2l</link>
      <guid>https://dev.to/vannsl/the-surprisingly-weird-world-of-favicons-and-how-to-survive-it-5a2l</guid>
      <description>&lt;p&gt;Not a year goes by without another "how to use favicon this year" article. Let's recap what has changed over the years: Favicons began as simple &lt;code&gt;.ico&lt;/code&gt; files introduced by Microsoft with Internet Explorer 5 in 1999. These were small, square icons, typically in resolutions of 16×16 or 32×32 pixels. The &lt;code&gt;.ico&lt;/code&gt; file format can support images up to 256×256 pixels. They were designed to appear in browser tabs, bookmarks, and address bars.&lt;/p&gt;

&lt;p&gt;To add a favicon to a web page, we add a line of code to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section of an HTML page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/x-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&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;Over time, the format evolved to support more modern web standards and higher resolution displays. Developers began using formats such as PNG, SVG, and even adaptive icons for mobile platforms, leading to the rise of specialized favicon generators and meta tag configurations. Modern favicons now often include multiple sizes and formats to accommodate different devices, operating systems, and browser requirements, becoming a small but surprisingly complex part of web development. So-called "Apple Touch Icon" sizes are designed to provide optimized icons for different Apple devices and screen resolutions, ensuring a crisp display when a user saves a website to their Home screen. Each size corresponds to specific devices - from older iPhones and iPads (e.g., 57×57, 72×72) to modern Retina displays (e.g., 120×120, 180×180) - and allows iOS to choose the most appropriate icon. So in 20 years, we went from having to worry about an &lt;code&gt;.ico&lt;/code&gt; file to a list that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"57x57"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-57x57.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"60x60"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-60x60.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"72x72"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-72x72.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"76x76"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-76x76.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"114x114"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-114x114.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"120x120"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-120x120.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"144x144"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-144x144.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"152x152"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-152x152.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"180x180"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/apple-icon-180x180.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"192x192"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/android-icon-192x192.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"32x32"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon-32x32.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"96x96"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon-96x96.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"16x16"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon-16x16.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/manifest.webmanifest"&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;Okay, I admit I cheated a bit in the code snippet above. When I tested the tool, it gave me a &lt;code&gt;manifest.json&lt;/code&gt; and not a &lt;code&gt;manifest.webmanifest&lt;/code&gt;. See footnote &lt;sup&gt;&lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fn1" id="fnref1" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/sup&gt; for more information. We will use the &lt;code&gt;.webmanifest&lt;/code&gt; file for the rest of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manifest
&lt;/h2&gt;

&lt;p&gt;Wow, that's a lot. So what is this &lt;code&gt;manifest.webmanifest&lt;/code&gt;? Let's take a look at a sample manifest file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vannsl's Blog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-36x36.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"36x36"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-48x48.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"48x48"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-72x72.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"72x72"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-96x96.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"96x96"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-144x144.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"144x144"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-192x192.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192x192"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;link rel="manifest" href="/manifest.webmanifest"&amp;gt;&lt;/code&gt; tag points to a &lt;strong&gt;Web App Manifest&lt;/strong&gt; file, which provides metadata about a web application in JSON format. This file contains information such as the name of the application, icons in different sizes, theme colors, display mode (full screen, standalone, etc.), and more. It's especially important for &lt;strong&gt;progressive web apps (PWAs)&lt;/strong&gt; because it allows browsers (especially on Android) to provide "add to home screen" functionality with proper icons and branding. More information about this can be found on the &lt;a href="https://web.dev/articles/add-manifest" rel="noopener noreferrer"&gt;web app manifest page&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;So... do you have to remember all the different formats and sizes? Write all the HTML yourself, create all the favicons? Of course not! But be careful about using NPM packages and libraries to create them for you. Let me explain why.&lt;/p&gt;

&lt;p&gt;When I built this blog with &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; by forking the &lt;a href="https://github.com/11ty/eleventy-base-blog" rel="noopener noreferrer"&gt;eleventy-base-blog repository&lt;/a&gt;, I also checked if there was a favicon plugin available. And I found &lt;a href="https://github.com/atomrc/eleventy-favicon" rel="noopener noreferrer"&gt;this one&lt;/a&gt;. This plugin has two bugs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Since the last update to the repository was 4 years ago, I think it's fair to say that it's deprecated. While this isn't a problem per se- (it really isn't!). Of course we can use "deprecated" software! But that's another topic for another time)-it unfortunately comes with vulnerable dependencies. So to use it, you'd have to fork it, update the dependencies yourself, make a pull request, wait for it to be published-or use your fork as a plugin.&lt;/li&gt;
&lt;li&gt;The README says: "Currently, the plugin does not generate the &lt;code&gt;manifest.json&lt;/code&gt; suggested by &lt;a href="https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs" rel="noopener noreferrer"&gt;this article&lt;/a&gt;". But as we learned above, we might want one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So... actually creating all the favicons and writing the HTML and manifest is it? No, still not! The rest of this article is another shout-out to the amazing &lt;a href="https://tiny-helpers.dev/" rel="noopener noreferrer"&gt;Tiny Helpers&lt;/a&gt; website by [Stefan Judis]. Because it's got you covered! You can use the &lt;a href="https://www.favicon-generator.org/?ref=tiny-helpers" rel="noopener noreferrer"&gt;Favicon Generator&lt;/a&gt;&lt;sup&gt;&lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fn2" id="fnref2" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/sup&gt; to convert an image from PNG to ICO, from JPG to ICO, or from GIF to ICO, as well as all application icons. The &lt;code&gt;.zip&lt;/code&gt; file also contains the manifest. You may also want to rename &lt;code&gt;manifest.json&lt;/code&gt;to&lt;code&gt;manifest.webmanifest&lt;/code&gt;&lt;sup&gt;&lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fn1" id="fnref1:1" rel="noopener noreferrer"&gt;1:1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  But wait—do we really need all of this?
&lt;/h2&gt;

&lt;p&gt;No, maybe not. In the code above, you can see that I used PNG files for favicons. But we have something better: SVGs. [SVG favicons are supported by some modern browsers (&lt;a href="https://caniuse.com/link-icon-svg" rel="noopener noreferrer"&gt;https://caniuse.com/link-icon-svg&lt;/a&gt;). And for browsers that do not support them, we can still add the &lt;code&gt;.ico&lt;/code&gt; file as a fallback. This again reduces the LOC to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"16x16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.svg"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"any"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/manifest.webmanifest"&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;Again, you can use the &lt;a href="https://www.favicon-generator.org/?ref=tiny-helpers" rel="noopener noreferrer"&gt;Favicon Generator&lt;/a&gt;&lt;sup&gt;&lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fn2" id="fnref2:1" rel="noopener noreferrer"&gt;2:1&lt;/a&gt;&lt;/sup&gt; to create the 16×16px fallback &lt;code&gt;.ico&lt;/code&gt; file. Again, it comes with a manifest file. However, I'd advise you to modify the HTML output the tool provides. When I tested it, the favicon Generator did not set the &lt;code&gt;sizes&lt;/code&gt; attribute of the &lt;code&gt;.ico&lt;/code&gt; file to &lt;code&gt;16x16&lt;/code&gt;, but to &lt;code&gt;any&lt;/code&gt;. This can cause Chrome to pick the &lt;code&gt;.ico&lt;/code&gt; file instead of the &lt;code&gt;.svg&lt;/code&gt; file &lt;a href="https://spacejelly.dev/posts/light-dark-mode-favicons" rel="noopener noreferrer"&gt;as Colby Fayock explained in his article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to make it as simple as it was in 1999, you can also just provide the SVG file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.svg"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&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;Browsers that do not support SVG favicons will create some sort of fallback. For example, this is the google.com favicon in a Chromium browser. And in Safari, the browser falls back to a simple "G" on a gray background. You can see this effect in the screenshot below, looking at the left tab in both browsers (Chrome on top, Safari on bottom). On the right tab, I opened &lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/https./vannsl.io" rel="noopener noreferrer"&gt;vannsl.io&lt;/a&gt;, which uses &lt;code&gt;.png&lt;/code&gt; favicons that work in both browsers.&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%2F8neo86ccvpoitdbda6fj.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%2F8neo86ccvpoitdbda6fj.png" alt="Google's and Vannsl.io's Favicons in a Chrome and Safari browser" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PNG vs. SVG: What to do?
&lt;/h2&gt;

&lt;p&gt;Personally, I will stick with the PNG solution. As I have shown, it is not difficult to create them with the tools provided. I prefer crisp and beautiful favicons on all devices and screen sizes. I don't want to see a 16×16px &lt;code&gt;.ico&lt;/code&gt; fallback on a 5K display. (Not that I have one 🤑) However, the SVG solution has better performance and maintainability. You don't have to worry about different resolutions anymore. The SVG file will scale and look good even on a 5K display. It is also very small in size and you will save some bytes, which might be what you prefer! There is another reason why you might want to go with SVGs - because you can play around with them, as described in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  SVG Favicons Dark Mode
&lt;/h2&gt;

&lt;p&gt;Using SVG means that you can add a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block. Adding &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; means you can add a dark mode. You can find a &lt;a href="https://svg-favicon-fdfb6d.gitlab.io/" rel="noopener noreferrer"&gt;demo here on GitLab&lt;/a&gt; along with its &lt;a href="https://gitlab.com/Vannsl/svg-favicon#" rel="noopener noreferrer"&gt;GitLab repository&lt;/a&gt; and a &lt;a href="https://vannsl.github.io/svg-favicon/" rel="noopener noreferrer"&gt;demo here on GitHub&lt;/a&gt; along with its &lt;a href="https://github.com/Vannsl/svg-favicon" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The favicon contains embedded CSS that changes colors based on the user's system color scheme (light or dark mode). This is achieved by using the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query within the SVG.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- The SVG code of this favicon originally came from the Random Favicon Generator: https://toolcool.org/random-favicon-generator/ and was modified to accept dark mode colors. --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"250px"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"250px"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"1.00"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;.favicon-fill&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;244&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;141&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.favicon-stroke&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;.favicon-fill&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nc"&gt;.favicon-stroke&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;244&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;141&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"favicon-fill"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;g&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"favicon-stroke"&lt;/span&gt; &lt;span class="na"&gt;transform=&lt;/span&gt;&lt;span class="s"&gt;"translate(2.40, 2.40) scale(0.8)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M0 0h24v24H0z"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;rx=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M4 12l3 -3c.928 -.893 2.072 -.893 3 0l4 4"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M13 12l2 -2c.928 -.893 2.072 -.893 3 0l2 2"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"14"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"7"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"14.01"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"7"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following images show the Safari, Chrome, and Firefox browsers (from left to right) in light and dark mode. Safari does &lt;strong&gt;not&lt;/strong&gt; use the SVG favicon, but an &lt;code&gt;.ico&lt;/code&gt; fallback. Chrome and Firefox show the SVG favicon in the correct color mode. Chrome requires a tab reload to update the icon when changing the color mode.&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%2Fgc81psf9eojxx5q4rg0m.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%2Fgc81psf9eojxx5q4rg0m.png" alt="Light mode SVG favicons demo page in Safari, Firefox, and Chrome (from left to right)" width="800" height="295"&gt;&lt;/a&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%2F3im77iizw9tjnaehm3ag.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%2F3im77iizw9tjnaehm3ag.png" alt="Dark mode SVG favicons demo page in Safari, Firefox, and Chrome (from left to right)" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following images show only the tabs with the favicons:&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%2Fhqbse5eryk39oown81x4.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%2Fhqbse5eryk39oown81x4.png" alt="Light mode SVG favicons in Safari, Firefox, and Chrome (from left to right)" width="800" height="37"&gt;&lt;/a&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%2Frflxddpogzy68i5aarff.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%2Frflxddpogzy68i5aarff.png" alt="Dark mode SVG favicons in Safari, Firefox, and Chrome (from left to right)" width="800" height="27"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If you're playing around with favicons, be sure to set the sizes correctly, as shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"48x48"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.svg"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"any"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&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;Otherwise, at least Chrome will take the &lt;code&gt;.ico&lt;/code&gt; fallback even though it could render an SVG favicon.&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;You might see &lt;code&gt;manifest.json&lt;/code&gt; and &lt;code&gt;manifest.webmanifest&lt;/code&gt; files. As &lt;a href="https://www.w3.org/TR/appmanifest/" rel="noopener noreferrer"&gt;defined in the specs&lt;/a&gt; it should be &lt;code&gt;.webmanifest&lt;/code&gt;. However, currently both formats are working. People &lt;a href="https://github.com/w3c/manifest/issues/689" rel="noopener noreferrer"&gt;discussed this on a W3C GitHub issue&lt;/a&gt;. I'd recommend using &lt;code&gt;.webmanifest&lt;/code&gt; because: a) it's what the specs say and b) (I think) VS Code can be configured to check &lt;code&gt;.webmanifest&lt;/code&gt; files explicitly against the schema needed for a manifest and therefore also provides autocompletions. I have not tested this though. &lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fnref1" rel="noopener noreferrer"&gt;↩︎&lt;/a&gt; &lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fnref1:1" rel="noopener noreferrer"&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn2"&gt;
&lt;p&gt;You can find these and other useful websites on &lt;a href="https://tiny-helpers.dev/" rel="noopener noreferrer"&gt;Tiny Helpers&lt;/a&gt; from &lt;a href="https://www.stefanjudis.com/" rel="noopener noreferrer"&gt;Stefan Judis&lt;/a&gt;. &lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fnref2" rel="noopener noreferrer"&gt;↩︎&lt;/a&gt; &lt;a href="https://blog.vannsl.io/blog/the-surprisingly-weird-world-of-favicons/#fnref2:1" rel="noopener noreferrer"&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>css</category>
      <category>svg</category>
    </item>
    <item>
      <title>Modern Image Formats on the Web: WebP and AVIF Without the Headache</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Mon, 07 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/vannsl/modern-image-formats-on-the-web-webp-and-avif-without-the-headache-37h8</link>
      <guid>https://dev.to/vannsl/modern-image-formats-on-the-web-webp-and-avif-without-the-headache-37h8</guid>
      <description>&lt;p&gt;Tired of seeing this Google Lighthouse diagnostic?&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%2F56cb3p8pxmnkzcfyi17l.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%2F56cb3p8pxmnkzcfyi17l.png" alt="Lighthouse diagnostic about using next gen image formats" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those fancy modern image formats that supposedly only work in Chrome? Maybe you've considered using progressive enhancement—serving AVIF to capable browsers and falling back to JPEGs for the rest—but it all sounds a bit too complicated?&lt;/p&gt;

&lt;p&gt;Let's take a proper look at what's possible in 2025 and how easy it actually is to use modern formats.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 HTML Implementation: The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; Element
&lt;/h2&gt;

&lt;p&gt;Let's start with the trusty &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
  &amp;lt;source type="image/avif" srcset="/image.avif 1000w"&amp;gt;
  &amp;lt;source type="image/webp" srcset="/image.webp 1000w"&amp;gt;
  &amp;lt;img loading="lazy" decoding="async" src="/image.jpeg" alt="My image" width="1000" height="1000"&amp;gt;
&amp;lt;/picture&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;&lt;/strong&gt; element allows the browser to choose the best image format: AVIF, WebP, or JPEG as a fallback. The &lt;strong&gt;&lt;code&gt;loading="lazy"&lt;/code&gt;&lt;/strong&gt; attribute delays loading the image until it's near the viewport, which helps improve page load speed. The &lt;strong&gt;&lt;code&gt;decoding="async"&lt;/code&gt;&lt;/strong&gt; attribute decodes the image in the background, allowing the page to render faster without waiting for the image to finish decoding. While the number &lt;code&gt;1000&lt;/code&gt; here is only an example, explicitly setting &lt;strong&gt;&lt;code&gt;width&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;height&lt;/code&gt;&lt;/strong&gt; helps the browser allocate the correct layout space &lt;em&gt;before&lt;/em&gt; the image loads, which improves visual stability and avoids layout shifts—especially important for Core Web Vitals. Additionally, the &lt;code&gt;srcset&lt;/code&gt; attribute allows the browser to choose the most appropriate image based on screen size and resolution.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://www.w3schools.com/tags/tag_picture.asp" rel="noopener noreferrer"&gt;w3schools&lt;/a&gt;, browser support for &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; is excellent.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 CSS Option: &lt;code&gt;image-set()&lt;/code&gt; for Backgrounds
&lt;/h2&gt;

&lt;p&gt;If you're dealing with background images instead of inline &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements, CSS offers a similar mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.element {
  background-image: image-set(
    url("image.avif") type("image/avif"),
    url("image.webp") type("image/webp"),
    url("image.jpg") type("image/jpeg")
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is supported in &lt;strong&gt;Safari 17+&lt;/strong&gt; , as seen on &lt;a href="https://caniuse.com/css-image-set" rel="noopener noreferrer"&gt;Can I use&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For most use cases, you'll be covered using either &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; for inline images or &lt;code&gt;image-set()&lt;/code&gt; for backgrounds. However, because browser support for &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; is more consistent, it's usually the safer choice. When possible, avoid using background images for critical content.&lt;/p&gt;

&lt;p&gt;In October 2024, I talked about the &lt;code&gt;image-set()&lt;/code&gt; CSS functional notation with my &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt; Co-Host &lt;a href="https://schepp.dev/" rel="noopener noreferrer"&gt;Schepp&lt;/a&gt; in the episode &lt;a href="https://workingdraft.de/635/" rel="noopener noreferrer"&gt;Revision 635: State of CSS 2024, Teil 3/3&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Converting Images to WebP and AVIF
&lt;/h2&gt;

&lt;p&gt;So how do you actually get your images into WebP or AVIF? While online tools exist, they're often a black box—you don't always know what optimizations or metadata stripping might be happening behind the scenes. Not to mention the privacy aspect of uploading your files.&lt;/p&gt;

&lt;p&gt;If you're more comfortable on the command line and prefer open-source tooling, you're in luck.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎬 Conversion via &lt;code&gt;ffmpeg&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;On macOS (or any system with ffmpeg installed), you can convert a JPEG to AVIF with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i image.jpg -c:v libaom-av1 -crf 30 -b:v 0 image.avif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what each part of this command does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ffmpeg&lt;/code&gt;:
This is the command-line tool for processing video, audio, and images. It supports a variety of formats and codecs, making it an excellent choice for converting media files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-i image.jpg&lt;/code&gt;:
This specifies the input file. In this case, we’re using image.jpg as the source file. The -i option tells ffmpeg which file to process.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c:v libaom-av1&lt;/code&gt;:
This option sets the codec to use for the video (or in this case, the image). libaom-av1 is the encoder for the AVIF format, specifically using the AV1 codec, which is known for excellent compression and quality.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-crf 30&lt;/code&gt;:
The Constant Rate Factor (CRF) controls the output quality. Lower CRF values result in better quality (and larger file sizes), while higher CRF values reduce the quality and the file size. In this case, 30 is a reasonable value for a balance between quality and file size. Typically, CRF values range from 0 (best quality) to 51 (worst quality).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-b:v 0&lt;/code&gt;:
This option controls the target bitrate. When set to 0, it tells ffmpeg to use variable bitrate encoding, where the bitrate adjusts based on the complexity of the image to maintain a consistent quality throughout. For AVIF, this is usually set to 0 for the best results.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;image.avif&lt;/code&gt;:
This is the output file. ffmpeg will take the input file image.jpg, convert it, and save it as image.avif.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  🧪 Conversion via Sharp (Node.js)
&lt;/h3&gt;

&lt;p&gt;Instead of manually converting files every time, consider scripting it—either as a part of your build pipeline or as a simple CLI tool. For this, create a script to convert your images in your project using &lt;a href="https://github.com/lovell/sharp" rel="noopener noreferrer"&gt;Sharp&lt;/a&gt;, a fast image processing library for Node.js.&lt;/p&gt;

&lt;p&gt;I published a repository &lt;a href="https://github.com/Vannsl/node-convert-sharp" rel="noopener noreferrer"&gt;node-convert-sharp&lt;/a&gt; with a basic script. Here's a streamlined version of such a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env node

import sharp from 'sharp';
import path from 'path';
import fs from 'fs/promises';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const inputPath = path.join(__dirname, 'input.jpg');
const outputDir = path.join(__dirname, 'output');
await fs.mkdir(outputDir, { recursive: true });

const baseName = path.basename(inputPath, path.extname(inputPath));

// Convert to WebP
try {
  await sharp(inputPath)
    .toFormat('webp')
    .toFile(path.join(outputDir, `${baseName}.webp`));
  console.log('✅ WebP success');
} catch (err) {
  console.error('❌ Error WebP:', err);
}

// Convert to AVIF
try {
  await sharp(inputPath)
    .toFormat('avif')
    .toFile(path.join(outputDir, `${baseName}.avif`));
  console.log('✅ AVIF success');
} catch (err) {
  console.error('❌ Error AVIF:', err);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧰 Running the Script
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install sharp:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure your &lt;code&gt;package.json&lt;/code&gt; includes:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make the script executable:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run it:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because of the &lt;code&gt;#!/usr/bin/env node&lt;/code&gt; shebang at the top, there's no need to call &lt;code&gt;node convert.js&lt;/code&gt; explicitly—the OS will handle it for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Using modern image formats doesn't have to be a headache. With good browser support, native HTML and CSS solutions, and reliable open-source conversion tools, there's little excuse not to start using WebP and AVIF today.&lt;/p&gt;

&lt;p&gt;Faster pages, smaller files, happier users.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Between Diapers and Development – How My Blog Came to Life with Eleventy</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Sun, 06 Apr 2025 06:27:55 +0000</pubDate>
      <link>https://dev.to/vannsl/between-diapers-and-development-how-my-blog-came-to-life-with-eleventy-10np</link>
      <guid>https://dev.to/vannsl/between-diapers-and-development-how-my-blog-came-to-life-with-eleventy-10np</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📝 Originally published on 2025-03-30 on my blog:&lt;br&gt;&lt;br&gt;
&lt;a href="https://blog.vannsl.io/blog/between-dialpers-and-development/between-diapers-and-development/" rel="noopener noreferrer"&gt;https://blog.vannsl.io/blog/between-dialpers-and-development/between-diapers-and-development/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;I worked continuously as a software developer for 13 years and 3 months. No gaps, not even a month-long break. And then? Suddenly, maternity leave and parental leave were upon me. Well, after an entire pregnancy, it wasn't &lt;em&gt;that&lt;/em&gt; sudden. But I still wasn’t prepared for months of "not working." And yes, every parent is probably laughing out loud at the phrase "parental leave" and "not working." Before, I thought a 40-hour job was full-time. Oh, how naïve.  &lt;/p&gt;

&lt;p&gt;Time during parental leave is limited, and life is completely dictated by the baby. When you eat, sleep, or shower—all of it revolves around the baby. And then, suddenly, there are unexpected windows of time when the baby sleeps longer than expected or someone else takes over. And you find yourself standing there, thinking: &lt;em&gt;"Now what?"&lt;/em&gt; Housework? Meh. Starting an 800-page fantasy epic? Too much commitment.  &lt;/p&gt;

&lt;p&gt;What I missed the most? Gaming and working. So, I started again. Just 7.5 weeks after giving birth, I ended my break from the &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt; podcast early and returned while the baby was with its dad. Still, I had plenty of time when the baby napped in the carrier during the day. Because during the day, sleeping only happens when tightly strapped to me in a carrier—preferably while I'm bouncing on a gym ball.  &lt;/p&gt;

&lt;p&gt;So, I divided my time 50/50 between game consoles and my laptop. Always with headphones, of course. But what was I actually doing on the laptop? I had always wanted to set up a tech blog! The real question was: why had I never managed to do it before giving birth?  &lt;/p&gt;

&lt;h2&gt;
  
  
  The "Right" Tool
&lt;/h2&gt;

&lt;p&gt;The frontend ecosystem offers countless frameworks for blogs. I wanted to take my time, test different tools, weigh the pros and cons, and make the best choice. On my mental shortlist were:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vitepress.dev/" rel="noopener noreferrer"&gt;VitePress&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://solidjs.com/" rel="noopener noreferrer"&gt;Solid&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://content.nuxt.com/" rel="noopener noreferrer"&gt;Nuxt Content&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://qwik.dev/" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VitePress and Nuxt Content because I usually work with Vue. Solid, Astro, and Qwik because I heard a lot about them at conferences. But are those the right criteria? Well, it depends. For companies, it might be relevant how many developers are interested in a framework—to ensure they can find enough skilled people for projects.  &lt;/p&gt;

&lt;p&gt;However, as we discussed in Revision 656 (link to come once the episode is released) of &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt;, we believe developers should be able to work with &lt;em&gt;any&lt;/em&gt; framework. Besides, I'd be working on this project alone.  &lt;/p&gt;

&lt;p&gt;To maximize learning, I could choose something new. Normally, I consider that a valid reason. But given my limited time, that wasn't a priority for me. Another criterion could be long-term viability: Is there a large core team and an active community? Well, who still remembers &lt;a href="https://angularjs.org/" rel="noopener noreferrer"&gt;AngularJS&lt;/a&gt;? From Google? And didn’t Facebook/Meta start &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;? I wouldn’t rely too much on that.  &lt;/p&gt;

&lt;p&gt;I built my portfolio website &lt;a href="https://vannsl.io" rel="noopener noreferrer"&gt;vannsl.io&lt;/a&gt; with &lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt; years ago—the predecessor of the &lt;a href="https://svelte.dev/docs/kit/introduction" rel="noopener noreferrer"&gt;SvelteKit&lt;/a&gt; framework. But guess what? It’s still running on Sapper. I never rewrote it because I had no real reason to.  &lt;/p&gt;

&lt;p&gt;So, my decisive criterion was simple: My baby is sleeping &lt;em&gt;now&lt;/em&gt;—so I need to move &lt;em&gt;fast.&lt;/em&gt; No long learning curves, no complex setup.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7304822118804811776?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7304822118804811776%2C7305105530983772162%29&amp;amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287305105530983772162%2Curn%3Ali%3Aactivity%3A7304822118804811776%29" rel="noopener noreferrer"&gt;As Tobias Struckmeier aptly put it&lt;/a&gt;:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Imho usually the software chosen doesn't matter a lot to my experience. It's the dedication and love put in and in case of a blog anyway the content that makes the gold."  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, I abandoned the idea of a grand evaluation. I needed a domain and hosting, but everything had to be as quick and pragmatic as possible.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Domain and Hosting
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://workingdraft.de/648/" rel="noopener noreferrer"&gt;Revision 648: Personal Web Sites&lt;/a&gt;, &lt;a href="https://matthiasott.com/" rel="noopener noreferrer"&gt;Matthias Ott&lt;/a&gt; discussed personal websites and the &lt;em&gt;Indie Web&lt;/em&gt; concept with my &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt; co-hosts &lt;a href="https://schepp.dev/" rel="noopener noreferrer"&gt;Schepp&lt;/a&gt; and &lt;a href="https://www.peterkroener.de/" rel="noopener noreferrer"&gt;Peter&lt;/a&gt;. The idea? Host your own content instead of relying on platforms like Medium or dev.to. Sounded great—but self-hosting? I simply didn’t have the time.  &lt;/p&gt;

&lt;p&gt;Buying a new domain? I had vanessaotto.de with &lt;a href="https://www.strato.de/" rel="noopener noreferrer"&gt;Strato&lt;/a&gt;, but only for email. My other domains were with &lt;a href="https://www.hover.com/" rel="noopener noreferrer"&gt;Hover&lt;/a&gt;. I noticed vanessaotto.com was still available on Strato. Tempting. But the thought of transferring domains or changing plans? No way.  &lt;/p&gt;

&lt;p&gt;Pragmatism won: The blog now runs on &lt;a href="https://blog.vannsl.io/" rel="noopener noreferrer"&gt;blog.vannsl.io&lt;/a&gt;, a subdomain of my already Netlify-hosted portfolio site. Setup time thanks to &lt;a href="https://docs.netlify.com/domains/set-up-netlify-dns/" rel="noopener noreferrer"&gt;Netlify DNS&lt;/a&gt;? Less than five minutes.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Tool Decision
&lt;/h2&gt;

&lt;p&gt;My main requirements:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Manage blog posts as Markdown files.
&lt;/li&gt;
&lt;li&gt;Work on it anytime without a steep learning curve.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I first tried &lt;a href="https://vitepress.dev/" rel="noopener noreferrer"&gt;VitePress&lt;/a&gt;, thinking it would be easy for me. I followed the &lt;a href="https://vitepress.dev/guide/getting-started" rel="noopener noreferrer"&gt;Getting Started Guide&lt;/a&gt;. But even during setup, I realized: I'd need time to explore themes, actions, and features. I’d need at least an afternoon with full focus. The baby was already stirring in the carrier—so no time for that.  &lt;/p&gt;

&lt;p&gt;Then I remembered &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt;. I had heard about it from people who don’t typically follow framework hypes. For example, &lt;a href="https://oida.dev/" rel="noopener noreferrer"&gt;Stefan Baumgartner’s blog&lt;/a&gt; and the &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft website&lt;/a&gt; both use it. That idea appealed to me. Perfect.  &lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.youtube.com/watch?v=kzf9A9tkkl4" rel="noopener noreferrer"&gt;YouTube tutorial&lt;/a&gt; showed me: I only need HTML, JSON, and Markdown. Eleventy automatically generates the routes. Exactly what I was looking for. And &lt;a href="https://schepp.dev/" rel="noopener noreferrer"&gt;Schepp&lt;/a&gt; told me that I could even &lt;a href="https://henry.codes/writing/how-to-use-vue-to-template-your-eleventy-projects/" rel="noopener noreferrer"&gt;integrate Vue later&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I browsed &lt;a href="https://11tybundle.dev/" rel="noopener noreferrer"&gt;11tybundle.dev&lt;/a&gt; for inspiration and quickly found a &lt;a href="https://github.com/11ty/eleventy-base-blog" rel="noopener noreferrer"&gt;starter template&lt;/a&gt; I liked.&lt;/p&gt;

&lt;p&gt;I heard rumors about the 100% Lighthouse Score of websites written with Eleventy before. I do not want to talk about Google Lighthouse scores in this article and swallow all my opinions, knowledge and thoughts about this topic now. So, a 100% score &lt;em&gt;should&lt;/em&gt; be the default for such a simple website loading basically only HTML and CSS, and maybe a font and one or two lazy loaded images. However, it's still nice to see the confetti after running Google Lighthouse:&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%2Flzwcyga1z42ietwh2r88.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%2Flzwcyga1z42ietwh2r88.png" alt="100% Lighthouse Score" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trade-offs
&lt;/h2&gt;

&lt;p&gt;Normally, I would have:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Picked a color palette from &lt;a href="https://colorhunt.co/" rel="noopener noreferrer"&gt;Color Hunt&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.
&lt;/li&gt;
&lt;li&gt;Checked contrast with &lt;a href="https://abc.useallfive.com/" rel="noopener noreferrer"&gt;Accessible Brand Colors&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.
&lt;/li&gt;
&lt;li&gt;Created a custom favicon &lt;a href="https://toolcool.org/random-favicon-generator/" rel="noopener noreferrer"&gt;Random Favicon Generator&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.
&lt;/li&gt;
&lt;li&gt;Set up SEO meta tags.
&lt;/li&gt;
&lt;li&gt;Chosen a nice font.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, I:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used the base project’s colors.
&lt;/li&gt;
&lt;li&gt;Recycled the favicon and primary colors from &lt;a href="https://vannsl.io/" rel="noopener noreferrer"&gt;vannsl.io&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;Meta tags and &lt;a href="https://ogp.me/" rel="noopener noreferrer"&gt;OG images&lt;/a&gt;? Uh, maybe later.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now? Now, I write. Between diaper changes and code. Thanks to &lt;a href="https://codingdad.temme.dev/" rel="noopener noreferrer"&gt;Stefan Temme&lt;/a&gt; for inspiring this article's title.  &lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;You can find these and other useful websites on &lt;a href="https://tiny-helpers.dev/" rel="noopener noreferrer"&gt;Tiny Helpers&lt;/a&gt; from &lt;a href="https://www.stefanjudis.com/" rel="noopener noreferrer"&gt;Stefan Judis&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>html</category>
    </item>
    <item>
      <title>Zwischen Wickeln und Entwickeln - Wie mein Blog mit Eleventy entstand</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Sun, 06 Apr 2025 06:20:53 +0000</pubDate>
      <link>https://dev.to/vannsl/zwischen-wickeln-und-entwickeln-wie-mein-blog-mit-eleventy-entstand-5gdm</link>
      <guid>https://dev.to/vannsl/zwischen-wickeln-und-entwickeln-wie-mein-blog-mit-eleventy-entstand-5gdm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📝 Ursprünglich veröffentlicht am 26.03.2025 auf meinem Blog:&lt;br&gt;&lt;br&gt;
&lt;a href="https://blog.vannsl.io/blog/zwischen-wickeln-und-entwicklen/" rel="noopener noreferrer"&gt;https://blog.vannsl.io/blog/zwischen-wickeln-und-entwicklen/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Vorwort
&lt;/h2&gt;

&lt;p&gt;13 Jahre und 3 Monate habe am Stück als Softwareentwicklerin gearbeitet. Lückenlos, nicht einmal ein Monat Pause. Und dann? Plötzlich stand Mutterschutz und Elternzeit an. Na gut, so plötzlich war es nach einer ganzen Schwangerschaft auch nicht. Aber vorbereitet auf mehrere Monate "nicht arbeiten" war ich dennoch nicht. Und ja, alle Eltern lachen nun laut auf bei den Worten "Elternzeit" und "nicht arbeiten". Ich dachte ja vorher ein 40-Stunden-Job sei Vollzeit. Hachja.&lt;/p&gt;

&lt;p&gt;Die Zeit in der Elternzeit ist begrenzt, und das Leben ist fremdgesteuert. Wann man selbst isst, schläft, oder duscht – alles richtet sich nach dem Baby. Wenn sich dann plötzlich Zeitfenster auftun, in denen das Baby unerwartet lange schläft oder jemand anderes es übernimmt, steht man erst einmal da und fragt sich: "Und jetzt?" Haushalt? Meh. Ein 800-seitiges Fantasy-Epos anfangen? Zu viel Commitment. Was mir am meisten fehlte? Zocken und Arbeiten. Also fing ich wieder damit an. Nur 7,5 Wochen nach der Geburt beendete ich daher vorzeitig meine Pause vom &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt; Podcast, und kehrte wieder zurück, während das Baby bei Papa war. Dennoch blieb mir doch noch recht viel Zeit, in der das Baby bei mir tagsüber in der Trage schlief. Denn tagsüber wird ausschließlich geschlafen, wenn es in der Trage fest an mich gezurrt ist und ich am besten noch dazu auf einem Gymnastikball bounce. Also teilte ich diese Zeit 50/50 zwischen Spielkonsolen und Laptop auf. Immer mit Kopfhörern, versteht sich. Aber was mache ich nun eigentlich am Laptop? Ich wollte doch schon immer mal einen Tech Blog aufsetzen! Die Frage war nur: Warum hatte ich das vor der Geburt nie geschafft?&lt;/p&gt;

&lt;h2&gt;
  
  
  Das "richtige" Tool
&lt;/h2&gt;

&lt;p&gt;Im Frontend-Ökosystem gibt es zahlreiche Frameworks, die sich für Blogs eignen. Ich wollte mir Zeit nehmen, verschiedene Tools zu testen, Vor- und Nachteile abzuwägen und die beste Wahl zu treffen. Auf meiner mentalen Liste standen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vitepress.dev/" rel="noopener noreferrer"&gt;VitePress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solidjs.com/" rel="noopener noreferrer"&gt;Solid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://content.nuxt.com/" rel="noopener noreferrer"&gt;Nuxt Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qwik.dev/" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VitePress und Nuxt Content, weil ich üblicherweise mit Vue arbeite. Solid, Astro und Qwik, weil ich auf Konferenzen viel über die Tools hörte. Doch sind das die richtigen Auswahlkriterien? Naja, es kommt darauf an. Für Firmen mag es relevant sein, wie viele Entwickler:innen sich für ein Framework interessieren, um genügend gute Leute für Projekte zu finden. Wobei, wie in Revision 656 (Link folgt, sobald die Episode released ist) des &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt; besprochen, finden wir, dass Entwickler:innen schon irgendwie mit allen Frameworks klarkommen sollten. Davon abgesehen, werde ich alleine an diesem Projekt arbeiten. Um einen Lerneffekt zu erzielen, könnte man etwas wählen, das man noch nicht kennt. Das finde ich normalerweise einen sehr validen Grund. Nur, was das aufgrund meiner begrenzten Zeit nicht für mich relevant. Ein anderes Kriterium, könnte sein, dass das Framework möglichst lange noch existiert und es ein großes Core-Team und eine nette Community hat. Na, wer erinnert sich noch an &lt;a href="https://angularjs.org/" rel="noopener noreferrer"&gt;AngularJS&lt;/a&gt;? Von Google? Und hat Facebook/Meta nicht &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; angefangen? Ich behaupte mal, da kann man sich nicht allzu sehr darauf verlassen. Meine Portfolio Webseite &lt;a href="https://vannsl.io" rel="noopener noreferrer"&gt;vannsl.io&lt;/a&gt; schrieb ich vor einigen Jahren mit &lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt;. Das ist der Vorgänger des Meta-Frameworks &lt;a href="https://svelte.dev/docs/kit/introduction" rel="noopener noreferrer"&gt;SvelteKit&lt;/a&gt; von &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;. Aber soll ich euch etwas sagen? Es läuft immernoch mit Sapper. Ich habe mich nie darum bemüht, es neu zu schreiben. Weil ich keinen richtigen Grund dafür habe. Für mich gab es also nur noch ein entscheidendes Kriterium: Mein Baby schläft jetzt – also muss es schnell gehen. Keine langen Lernkurven, keine komplexe Setup-Phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7304822118804811776?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7304822118804811776%2C7305105530983772162%29&amp;amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287305105530983772162%2Curn%3Ali%3Aactivity%3A7304822118804811776%29" rel="noopener noreferrer"&gt;Wie Tobias Struckmeier treffend sagte&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Imho usually the software chosen doesn't matter a lot to my experience. It's the dedication and love put in and in case of a blog anyway the content that makes the gold."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also ließ ich den Gedanken an eine große Evaluierung fallen. Ich brauchte eine Domain und ein Hosting, aber alles musste so schnell und pragmatisch wie möglich gehen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain und Hosting
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://workingdraft.de/648/" rel="noopener noreferrer"&gt;Revision 648: Personal Web Sites&lt;/a&gt; spricht &lt;a href="https://matthiasott.com/" rel="noopener noreferrer"&gt;Matthias Ott&lt;/a&gt; mit meinen &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft&lt;/a&gt; Cohosts &lt;a href="https://schepp.dev/" rel="noopener noreferrer"&gt;Schepp&lt;/a&gt; und &lt;a href="https://www.peterkroener.de/" rel="noopener noreferrer"&gt;Peter&lt;/a&gt; über das Erstellen persönlicher Webseiten und das Konzept des Indie Webs. Die Idee: Inhalte selbst hosten, statt auf Plattformen wie Medium oder dev.to zu posten. Klingt gut, aber Selbsthosting? Dafür fehlte mir schlicht die Zeit.&lt;/p&gt;

&lt;p&gt;Eine neue Domain kaufen? vanessaotto.de hatte ich zwar bei &lt;a href="https://www.strato.de/" rel="noopener noreferrer"&gt;Strato&lt;/a&gt;, aber in einem Tarif, der nur für E-Mails taugt. Meine anderen Domains liegen bei &lt;a href="https://www.hover.com/" rel="noopener noreferrer"&gt;Hover&lt;/a&gt;. Auf Strato sah ich, dass vanessaotto.com auch noch frei wäre. Verlockend. Die Idee, jetzt noch Domains zu transferieren oder Tarife zu ändern, ließ ich schnell fallen. Pragmatismus siegt: Der Blog läuft jetzt auf &lt;a href="https://blog.vannsl.io/" rel="noopener noreferrer"&gt;blog.vannsl.io&lt;/a&gt;, einer Subdomain meiner bereits auf Netlify gehosteten Portfolio-Seite. Setup-Zeit dank &lt;a href="https://docs.netlify.com/domains/set-up-netlify-dns/" rel="noopener noreferrer"&gt;Netlify DNS&lt;/a&gt;? Weniger als fünf Minuten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Die Tool-Entscheidung
&lt;/h2&gt;

&lt;p&gt;Meine Hauptanforderungen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Blogartikel als Markdown-Dateien verwalten.&lt;/li&gt;
&lt;li&gt;Jederzeit daran arbeiten können, ohne mich lange reinfinden zu müssen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Erst probierte ich &lt;a href="https://vitepress.dev/" rel="noopener noreferrer"&gt;VitePress&lt;/a&gt; aus. Ich dachte, das sollte mir leicht fallen. Ich ging ein paar Schritte durch den &lt;a href="https://vitepress.dev/guide/getting-started" rel="noopener noreferrer"&gt;Getting Started Guide&lt;/a&gt;. Schon beim Setup merkte ich: Ich müsste mich mit Themes, Actions und Features beschäftigen. Dafür bräuchte ich zumindest einen Nachmittag mit vollem Fokus Zeit. Das Baby wackelte aber bereits in der Trage – also keine Zeit für langes Einarbeiten. Noch nicht einmal in vermeintlich einfache Tools.&lt;/p&gt;

&lt;p&gt;Da erinnerte ich mich an &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt;. Ich hatte es immer wieder von Leuten gehört, die normalerweise keine Framework-Hypes mitmachen. Zum Beispiel laufen &lt;a href="https://oida.dev/" rel="noopener noreferrer"&gt;Stefan Baumgartners Blog&lt;/a&gt; und die &lt;a href="https://workingdraft.de/" rel="noopener noreferrer"&gt;Working Draft Webseite&lt;/a&gt; darauf. Und den Gedanken etwas zu nutzen, das ich nicht mit einem Hype verband, fand ich erstaunlich verlockend. Perfekt.&lt;/p&gt;

&lt;p&gt;Ein &lt;a href="https://www.youtube.com/watch?v=kzf9A9tkkl4" rel="noopener noreferrer"&gt;YouTube-Tutorial&lt;/a&gt; zeigte mir: Ich brauche nur HTML, JSON und Markdown. Die Routen generiert Eleventy automatisch. Exakt das, was ich suchte. Und von &lt;a href="https://schepp.dev/" rel="noopener noreferrer"&gt;Schepp&lt;/a&gt; erfuhr ich, dass es möglich sei, &lt;a href="https://henry.codes/writing/how-to-use-vue-to-template-your-eleventy-projects/" rel="noopener noreferrer"&gt;Vue später mit in das Projekt hinzuzufügen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Zur Inspiration ging ich auf die Webseite &lt;a href="https://11tybundle.dev/" rel="noopener noreferrer"&gt;11tybundle.dev&lt;/a&gt;. Diese beinhaltet einen guten Überblick über aktuelle Releases von Plugins, Blogartikeln, und Webseiten, die Eleventy im Einsatz haben.  Und für meine Situation perfekt: Es beinhaltet eine Liste an 27 Starter-Projekten, davon 10, die explizit für Blogs gedacht sind. Ich fand ich schnell ein &lt;a href="https://github.com/11ty/eleventy-base-blog" rel="noopener noreferrer"&gt;Starter-Template&lt;/a&gt;, das mir gefiel.&lt;/p&gt;

&lt;p&gt;Ich habe schon Gerüchte über den 100%-Lighthouse-Score von Websites gehört, die mit Eleventy erstellt wurden. In diesem Artikel möchte ich allerdings nicht über Google-Lighthouse-Scores sprechen und verschlucke daher all meine Meinungen, mein Wissen und meine Gedanken zu diesem Thema. Ein 100%-Score sollte bei so einer einfachen Website, die im Grunde nur HTML und CSS lädt – vielleicht noch eine Schriftart und ein oder zwei lazy geladene Bilder –, eigentlich der Standard sein. Trotzdem ist es schön, nach dem Ausführen von Google Lighthouse das Konfetti zu sehen.&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%2Funktos40of8y1qly20s3.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%2Funktos40of8y1qly20s3.png" alt="100% Lighthouse Score" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Die Abstriche
&lt;/h2&gt;

&lt;p&gt;Normalerweise hätte ich:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eine Farbpalette über &lt;a href="https://colorhunt.co/" rel="noopener noreferrer"&gt;Color Hunt&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt; gesucht.&lt;/li&gt;
&lt;li&gt;Kontraste mit &lt;a href="https://abc.useallfive.com/" rel="noopener noreferrer"&gt;Accessible Brand Colors&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt; getestet.&lt;/li&gt;
&lt;li&gt;Ein individuelles Favicon erstellt (siehe &lt;a href="https://toolcool.org/random-favicon-generator/" rel="noopener noreferrer"&gt;Random Favicon Generator&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;) .&lt;/li&gt;
&lt;li&gt;Meta-Tags für SEO gesetzt.&lt;/li&gt;
&lt;li&gt;Eine schöne Font gewählt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stattdessen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Farben größtenteils vom Base-Projekt übernommen.&lt;/li&gt;
&lt;li&gt;Das Favicon und primäre Farben von &lt;a href="https://vannsl.io/" rel="noopener noreferrer"&gt;vannsl.io&lt;/a&gt; recycelt.&lt;/li&gt;
&lt;li&gt;Meta-Tags und &lt;a href="https://ogp.me/" rel="noopener noreferrer"&gt;OG Images&lt;/a&gt;? Ähm. Kommen später. Vielleicht.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Das Layout ist simpel, und der einzige CSS-Breakpoint sitzt in der Welcome-Box der Startseite. Das Profilbild wird mobil über der Headline und auf Desktop rechts davon angezeigt. Das einzige andere selbst geschriebene CSS Feature ist die kurze Transition des Links in dieser Welcome-Box. Dazu inspirierte mich die Gestaltung der Links von &lt;a href="https://www.hansreinl.de/" rel="noopener noreferrer"&gt;Hans Christian Reinl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Und jetzt? Jetzt schreibe ich. Zwischen Wickeltisch und Code. An dieser Stelle sei noch &lt;a href="https://codingdad.temme.dev/" rel="noopener noreferrer"&gt;Stefan Temme&lt;/a&gt; gedankt, der für den &lt;a href="https://elk.zone/ruhr.social/@codingdad/109341442131075671" rel="noopener noreferrer"&gt;Titel dieses Artikels gesorgt hat&lt;/a&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Diese und noch viele weitere nützliche Tools findet ihr bei der Webseite &lt;a href="https://tiny-helpers.dev/" rel="noopener noreferrer"&gt;Tiny Helpers&lt;/a&gt; von &lt;a href="https://www.stefanjudis.com/" rel="noopener noreferrer"&gt;Stefan Judis&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>html</category>
    </item>
    <item>
      <title>Webpacker, Vue 3, and TypeScript</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Wed, 17 Mar 2021 08:07:16 +0000</pubDate>
      <link>https://dev.to/vannsl/webpacker-vue-3-and-typescript-1i99</link>
      <guid>https://dev.to/vannsl/webpacker-vue-3-and-typescript-1i99</guid>
      <description>

&lt;p&gt;Versions:&lt;/p&gt;

&lt;p&gt;Webpacker 5&lt;br&gt;
Rails 6&lt;br&gt;
Vue 3&lt;/p&gt;


&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;Compared to Vue 2, Vue 3 is written in TypeScript. As we're used to, the official Vue Documentation is one of the best sources to find out more about &lt;a href="https://v3.vuejs.org/guide/typescript-support.html#official-declaration-in-npm-packages" rel="noopener noreferrer"&gt;how to configure TypeScript in Vue 3&lt;/a&gt;. Something that can bother is that most tutorials use the &lt;a href="https://cli.vuejs.org/" rel="noopener noreferrer"&gt;Vue CLI&lt;/a&gt; to show how simple TypeScript can be added to the codebase. Although the Vue CLI is a powerful tool and it is actually as simple as running one command to add TypeScript, not each project has the possibility to be configured with the CLI or &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;. This article explains how to add TypeScript to Vue 3 applications within &lt;a href="https://github.com/rails/webpacker" rel="noopener noreferrer"&gt;Webpacker&lt;/a&gt;, the &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; gem for &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Ruby on Rails&lt;/a&gt; Fullstack applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to configure TypeScript in Vue 3
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. TS Loader
&lt;/h3&gt;

&lt;p&gt;To install the TypeScript Loader, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add ts-loader
&lt;span class="c"&gt;# or npm ts-loader&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. TS Config
&lt;/h3&gt;

&lt;p&gt;In the root directory of the Rails App, create the file &lt;code&gt;tsconfig.json&lt;/code&gt;. The following JSON is an example of the configuration you could add. Of course, your settings might differ from those. Make sure to adapt the paths which files to &lt;strong&gt;include&lt;/strong&gt; in the TypeScript Compilation (&lt;code&gt;app/javascript/src/**/*.ts&lt;/code&gt; and &lt;code&gt;app/javascript/src/**/*.vue&lt;/code&gt;) depending on your folder structure. Same for the &lt;strong&gt;alias&lt;/strong&gt; in the paths settings (&lt;code&gt;app/javascript/src/*&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jsx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"preserve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"importHelpers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowSyntheticDefaultImports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"webpack-env"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"app/javascript/src/*"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"dom.iterable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"scripthost"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"app/javascript/src/**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"app/javascript/src/**/*.vue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HAVE YOU CHECKED THE PATHS? NO? READ ABOVE THE CONFIG AFTER COPY/PASTING! ;)&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Webpack Loader
&lt;/h3&gt;

&lt;p&gt;As explained in a previous article about &lt;a href="https://dev.to/vannsl/vue3-on-rails-l9d"&gt;How to add Vue 3 in Rails&lt;/a&gt; I put all webpack loaders in a folder called &lt;code&gt;config/webpack/loaders&lt;/code&gt;. You can also create your loaders inline.&lt;/p&gt;

&lt;p&gt;The Loader Configuration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;tsx$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;appendTsSuffixTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;vue$/&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/node_modules/&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 the webpack configuration &lt;code&gt;config/environment.js&lt;/code&gt; add the loader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./loaders/ts&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="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only for reference, this is how my full webpack configuration looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rails/webpacker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VueLoaderPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DefinePlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./loaders/vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./loaders/ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app/javascript/src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~libs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app/javascript/lib&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~images&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app/javascript/images&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VueLoaderPlugin&lt;/span&gt;&lt;span class="dl"&gt;"&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;VueLoaderPlugin&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Define&lt;/span&gt;&lt;span class="dl"&gt;"&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;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;__VUE_OPTIONS_API__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;__VUE_PROD_DEVTOOLS__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitChunks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Shims
&lt;/h3&gt;

&lt;p&gt;To get the TypeScript Support working in Vue Single File Components, they have to be defined as a component. Quoting the official documentation about &lt;a href="https://v3.vuejs.org/api/global-api.html" rel="noopener noreferrer"&gt;defineCompinent&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Implementation-wise defineComponent does nothing but return the object passed to it. However, in terms of typing, the returned value has a synthetic type of a constructor for manual render function, TSX, and IDE tooling support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your folder where your Vue Applications are located (e.g. &lt;code&gt;app/javascript/src&lt;/code&gt;), add the file &lt;code&gt;shims-vue.d.ts&lt;/code&gt; to add the Shim:&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="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DefineComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DefineComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Linters and IDE Helpers
&lt;/h3&gt;

&lt;p&gt;This is up to you. I use ESLint and Prettier. For IDE Support I switched from &lt;a href="https://marketplace.visualstudio.com/items?itemName=octref.vetur" rel="noopener noreferrer"&gt;Vetur&lt;/a&gt; to &lt;a href="https://marketplace.visualstudio.com/items?itemName=znck.vue-language-features" rel="noopener noreferrer"&gt;Vue DX&lt;/a&gt;, but I can't strongly agree that you should do the same. The third member of the IDE Party is &lt;a href="https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar" rel="noopener noreferrer"&gt;Volar&lt;/a&gt;, which I can totally recommend for pure Vue3+TS Projects, especially if you use the &lt;code&gt;&amp;lt;script setup&amp;gt;&lt;/code&gt; syntactic sugar for using the &lt;a href="https://v3.vuejs.org/guide/composition-api-introduction.html" rel="noopener noreferrer"&gt;Composition API&lt;/a&gt;. Try them out and check what works best for you.&lt;/p&gt;

&lt;p&gt;🎉 &lt;strong&gt;You're done!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vue and TS
&lt;/h3&gt;

&lt;p&gt;Using Typescript in &lt;code&gt;.vue&lt;/code&gt; files require the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;lang="ts"&lt;/code&gt; to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;Import &lt;code&gt;defineComponent&lt;/code&gt; from &lt;code&gt;vue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Export the component as &lt;code&gt;defineComponent&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&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;defineComponent&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;h3&gt;
  
  
  Type Declarations
&lt;/h3&gt;

&lt;p&gt;Types can be found in file &lt;code&gt;types.ts&lt;/code&gt; the source directory:&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/javascript/src/types.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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;Import types from that 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;Item&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/types&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;h3&gt;
  
  
  Vue Data and Properties
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Type Assertions
&lt;/h4&gt;

&lt;p&gt;Using the &lt;code&gt;as&lt;/code&gt; keyword objects were able to override empty objects types to a real type:&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;futureItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;

&lt;span class="nx"&gt;futureItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;futureItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Vue Reactive Data Options API
&lt;/h4&gt;

&lt;p&gt;With that, we can assign types to data attributes in &lt;code&gt;.vue&lt;/code&gt; files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/types&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;defineComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&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;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Vue Reactive Data Composition API
&lt;/h4&gt;

&lt;p&gt;TODO :)&lt;/p&gt;

&lt;h4&gt;
  
  
  Vue Properties
&lt;/h4&gt;

&lt;p&gt;The same does not simply work for Vue Properties. Using the &lt;code&gt;PropType&lt;/code&gt;, Generics are set for custom properties.&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;// Before&lt;/span&gt;
&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Won't work&lt;/span&gt;
&lt;span class="nl"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// this is not valid JavaScript&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Won't work&lt;/span&gt;
&lt;span class="nl"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// valid JavaScript, but no generic&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Works&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;defineComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PropType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&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;Item&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PropType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Vue Computed Properties and Methods Options API
&lt;/h4&gt;

&lt;p&gt;Computed Properties and Methods don't need special TypeScript Handling in Vue.&lt;br&gt;
Types can be applied as usual in TypeScript:&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;defineComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PropType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&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;Item&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/types&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;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// Method Parameter types&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="nx"&gt;newItem&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;// Computed Property Return Item&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;firstItem&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Eh, and now?
&lt;/h2&gt;

&lt;p&gt;To learn how to use TypeScript ➡️ (e)Book &lt;a href="https://www.smashingmagazine.com/printed-books/typescript-in-50-lessons/#:~:text=In%20TypeScript%20in%2050%20Lessons,explained%2C%20from%20start%20to%20finish." rel="noopener noreferrer"&gt;TypeScript in 50 lessons&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>webpack</category>
      <category>rails</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Nevertheless, Vannsl Coded, and Talked, and Wrote</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Mon, 08 Mar 2021 15:54:56 +0000</pubDate>
      <link>https://dev.to/vannsl/nevertheless-vannsl-coded-and-talked-and-wrote-1e8</link>
      <guid>https://dev.to/vannsl/nevertheless-vannsl-coded-and-talked-and-wrote-1e8</guid>
      <description>&lt;h1&gt;
  
  
  How it's like to be a Woman in Tech AMA
&lt;/h1&gt;

&lt;p&gt;You ask I answer: &lt;a href="https://twitter.com/vannsl/status/1368910549740888064" rel="noopener noreferrer"&gt;Twitter Call AMA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the biggest shit you had to deal with as a public speaker?
&lt;/h2&gt;

&lt;p&gt;I like the way the writer has phrased this question. It doesn't ask for "if" but "the biggest". There are several examples, but it comes down to three main shits.&lt;/p&gt;

&lt;p&gt;Ok, I decided to talk first about the "Designer" question. That might also happen to males as well if they studied Mediainformatics as I did:&lt;/p&gt;

&lt;h3&gt;
  
  
  You're a designer
&lt;/h3&gt;

&lt;p&gt;The first is that I am &lt;em&gt;not only&lt;/em&gt; a developer, I have also a background in Human-Computer Interaction. That's a fancy way of saying: I had 4 semesters of psychology courses with a strong focus on Application Design. That's about how to structure components on webpages so they work on mobile and desktop. It's about UX Flows and Accessibility. That's why on some of my portfolios it says that I have a focus on UX Design ADDITIONALLY to being a programmer. I can't tell you how many times people tell in my face: "Hey it's so great that you're talking about Vue.js. I mean, for a designer!"&lt;/p&gt;

&lt;h3&gt;
  
  
  Conference Organizers
&lt;/h3&gt;

&lt;p&gt;Countless times I received emails such as: "Do you want to speak at our event? We need a woman" I'm not super mad at this. In most cases, people actually meant it friendly, they were just bad with words. What was rather funny happened at a &lt;strong&gt;web&lt;/strong&gt; conference, you know, web development. The thing with computers and stuff. My lanyard said "Vortragender", the German word for "male speaker". In the german language, the default words are usually male. It's different than in English. A developer can be male or female. But in German, we have explicit female endings. It would have been "Vortragende". Since it was on a conference that explicitly asked for a female speaker, I asked why on earth it says the male form. They answered with: "Oh, you know, we asked the (male) PHP developer 4 months ago but he said that he can't change it in the database". It took me a few seconds to progress that. But, apart from all the obvious things I could have asked now, I asked: Why haven't you manually written and printed my lanyard. Without the database. Or just hand-written it. They could not answer. &lt;strong&gt;They don't have a gender field in their database table of speakers. Every person is male by default.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ok, so those were the funny parts. Now to the hurtful memories. When sitting at the speaker dinner, female speakers is usually a topic that comes up once in the evening. The funny thing is, that often males discuss is, sitting next to me, and don't ask for my opinion. Not all of course. The worst thing that happened was a person that was ugly inside. He talked about a small conference he is organizing himself and that he has to say it's blind voting, but obviously, it isn't. Because if it would be blind voting, all speakers would be male, and "someone would kick him in the balls for that". But there is no way that a female would be voted. They are just not as good as the men. Btw, two female speakers were sitting with him at the same table. It was awful.&lt;/p&gt;

&lt;p&gt;I heard stories about speaker rooms that female speakers get asked if they're looking for their husband (or worse things). It didn't happen to me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conference Visitors
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;"That talk was really great!!! Especially for a woman!"&lt;/li&gt;
&lt;li&gt;"Wow, you don't look like usual female developers. I mean, you don't look like a male."&lt;/li&gt;
&lt;li&gt;"You're a designer, right?"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;I am so sorry for all designers. I'm SO bad at UI Design. UX Design and Frontend Development are my strengths, but I don't have much practice in UI Design. I'm sorry that people tell me that I would be a designer like it is something less worthy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once I spoke at a "why speak at conferences" MeetUp. I had one personal photo in the beginning. It showed me doing a Spartan Obstacle Race. My story to that was that this event gave me so much confidence that I could translate into my job. On the MeetUp page, attendees could give feedback. One was "next time more personal pictures please". WHAT?!&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you feel to be the only woman in an all-male podcast line-up?
&lt;/h2&gt;

&lt;p&gt;Oh, very comfortable. You know, I am used to be the only female in all-male situations. The difference is the people. I was in situations with all-males because they scared females away. Of course, that's not nice. As long people are good people, I really don't care about their gender, race, age, whatever. If they are not nice, I don't like them. Simple as that. But the truth is, that I am not the only woman in the Podcast. We have a female person who does the post-production of the episodes. Unfortunately, you won't hear her on tape, but I know that she is there :)&lt;/p&gt;

&lt;h2&gt;
  
  
  What were your biggest challenges in taking/having leadership in software projects?
&lt;/h2&gt;

&lt;p&gt;People have just a bit less trust or confidence than others would believe me. Or that I convince people about something. What I had to deal with was strategic discussions when it came to clients. Women (not men) thought it would be a smarter move to send a male developer, who had way less experience with the project as I did, to meetings about the planning of resources (Resources a.k.a real people).&lt;/p&gt;

&lt;p&gt;Btw: Surprisingly, a lot of women have told me that I am not allowed to have a problem with the word "guys" because it's genderless. I don't respond to that anymore, I'm too tired. What happened at some company parties (before Corona) when people didn't know what I do for a living, they ask: "Are you doing Marketing?" 🤷‍♀️&lt;/p&gt;

&lt;p&gt;Apart from this, I was really lucky. Most of the companies I worked for valued me for what I am: an incredible developer and wonder woman ;)&lt;/p&gt;

</description>
      <category>wecoded</category>
      <category>womenintech</category>
    </item>
    <item>
      <title>Vue3 on Rails</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Sun, 11 Oct 2020 17:09:48 +0000</pubDate>
      <link>https://dev.to/vannsl/vue3-on-rails-l9d</link>
      <guid>https://dev.to/vannsl/vue3-on-rails-l9d</guid>
      <description>

&lt;p&gt;Versions:&lt;/p&gt;

&lt;p&gt;Rails 6&lt;br&gt;
Vue 3&lt;/p&gt;


&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;At work, I do the frontend for the project &lt;a href="https://zage.life" rel="noopener noreferrer"&gt;ZAGE&lt;/a&gt; by &lt;a href="https://www.thelifecompany.com" rel="noopener noreferrer"&gt;The Life Company&lt;/a&gt;. The application is written in Rails. As I joined, I wanted to add Vue for some parts of the Frontend. But I couldn't find any tutorial on how to add Vue 3 to Rails. So, I wrote that tutorial, I was looking for.&lt;/p&gt;
&lt;h2&gt;
  
  
  What you should know at the end
&lt;/h2&gt;

&lt;p&gt;This article explains how to install Vue 3 in a Ruby on Rails app. In the end, you should be able to implement a Vue app within an ERB view template. The code of this tutorial can be found in my Github Repository &lt;a href="https://github.com/Vannsl/rails-vue3-app" rel="noopener noreferrer"&gt;rails-vue3-app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Content:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Create Rails app

&lt;ul&gt;
&lt;li&gt;Setup Rails app&lt;/li&gt;
&lt;li&gt;Install yarn&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Install Vue3 &amp;amp; Co.&lt;/li&gt;
&lt;li&gt;
Configure Webpack environment

&lt;ul&gt;
&lt;li&gt;Set an alias&lt;/li&gt;
&lt;li&gt;Vue Loader&lt;/li&gt;
&lt;li&gt;Include .vue files&lt;/li&gt;
&lt;li&gt;Set Vue properties&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Create Vue app

&lt;ul&gt;
&lt;li&gt;Create entry point and SFC&lt;/li&gt;
&lt;li&gt;Create Home controller and view as root route&lt;/li&gt;
&lt;li&gt;Connect Vue and Rails&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Next Steps&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Introduction
&lt;/h2&gt;

&lt;p&gt;Evan You released the &lt;a href="https://www.npmjs.com/package/vue/v/3.0.0" rel="noopener noreferrer"&gt;Vue 3&lt;/a&gt; in September 2020. This article does neither focus on the new features like the Composition API nor explain nor explain how to migrate from Vue2 to Vue3. Check out the &lt;a href="http://v3.vuejs.org" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; and &lt;a href="https://v3.vuejs.org/guide/migration/introduction.html" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; for that.&lt;/p&gt;

&lt;p&gt;While the Vue CLI and Vite are great tools to configure new Vue projects easily, current resources lack information on how to install Vue3 in existing codebases. When you have a Rails 5+ full-stack application, you most likely already have a &lt;a href="https://github.com/rails/webpacker" rel="noopener noreferrer"&gt;webpacker&lt;/a&gt; configuration. As of the date I'm writing this article, Webpacker offers a skeleton for Vue 2 via &lt;code&gt;rails webpacker:install:vue&lt;/code&gt;, but not for Vue 3 yet. I opened a PR, check the state &lt;a href="https://github.com/rails/webpacker/pull/2759" rel="noopener noreferrer"&gt;here&lt;/a&gt;. So, let's dive right into how to add Vue3 into your tech stack.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Create Rails app (optional)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Setup rails app
&lt;/h3&gt;

&lt;p&gt;To test the setup before adding it to your "real" codebase, you can create a new rails app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new rails_vue3_app &lt;span class="nt"&gt;--webpack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I'm not a rails developer, so feel free to give me tips and hints, if there are other best practices for rails specific code and commands.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Install yarn
&lt;/h3&gt;

&lt;p&gt;If the output of the previous command says something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Yarn not installed. Please download and &lt;span class="nb"&gt;install &lt;/span&gt;Yarn from https://yarnpkg.com/lang/en/docs/install/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...you need to install yarn and install the packages afterward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; yarn
&lt;span class="nb"&gt;cd &lt;/span&gt;rails_vue3_app
yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Install Vue3 &amp;amp; Co.
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you run your rails app inside of Docker, you don't need to install anything on your host machine. You can install the packages within your docker container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use Vue3, you'll need (guess what :) ) Vue in version 3, Vue-Loader in version 16 beta, and the SFC compiler.&lt;/p&gt;

&lt;p&gt;Vue3 is released on npm with the tag &lt;code&gt;next&lt;/code&gt;. The &lt;code&gt;current&lt;/code&gt; version is still 2.6.x to prevent developers from having to migrate to Vue3 if they don't want to. The same applies to the &lt;a href="https://www.npmjs.com/package/vue-loader" rel="noopener noreferrer"&gt;vue-loader&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in rails_vue3_app&lt;/span&gt;
yarn add vue@next vue-loader@next @vue/compiler-sfc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;code&gt;package.json&lt;/code&gt; to see the installed versions. The minimal versions should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;##############################&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;##############################&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rails_vue_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@vue/compiler-sfc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vue-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^16.0.0-beta.8"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Webpack environment configuration
&lt;/h2&gt;

&lt;p&gt;Next, we need to tell Webpack what to do with &lt;code&gt;*.vue&lt;/code&gt; files. For that, go to the file &lt;code&gt;webpack/environment.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;By default, it should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// webpack/environment.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rails/webpacker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Set an alias (optional)
&lt;/h3&gt;

&lt;p&gt;I like to put my Vue applications into a separate folder. I also want to use a Webpack alias for an easier path handling when importing files. I did that with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// webpack/environment.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// const { environment } = require('@rails/webpacker')&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
    &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app/javascript/src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// module.exports = environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Add Vue Loader
&lt;/h3&gt;

&lt;p&gt;Now it's time to add the loader. It tells Webpack what to do with files that match to the Regex &lt;code&gt;.vue&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// webpack/environment.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;

&lt;span class="c1"&gt;// const { environment } = require('@rails/webpacker')&lt;/span&gt;
&lt;span class="c1"&gt;// const path = require('path')&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VueLoaderPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// const customConfig = {&lt;/span&gt;
&lt;span class="c1"&gt;//   resolve:{&lt;/span&gt;
&lt;span class="c1"&gt;//     alias: {&lt;/span&gt;
&lt;span class="c1"&gt;//      '@': path.resolve(__dirname, '..', '..', 'app/javascript/src')&lt;/span&gt;
&lt;span class="c1"&gt;//     }&lt;/span&gt;
&lt;span class="c1"&gt;//   }&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="c1"&gt;// environment.config.merge(customConfig)&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VueLoaderPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&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;VueLoaderPlugin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;vue$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// module.exports = environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because I like to keep the file &lt;code&gt;webpack/environment.js&lt;/code&gt; as clean and readable as possible, I outsourced the configuration of Vue in an own file within the folder &lt;code&gt;webpack/loaders&lt;/code&gt;. By default it does not exist, so create it first using the terminal or your IDE. The end result should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// webpack/loaders/vue.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;vue$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-loader&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// webpack/environment.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;

&lt;span class="c1"&gt;// const { environment } = require('@rails/webpacker')&lt;/span&gt;
&lt;span class="c1"&gt;// const path = require('path')&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VueLoaderPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./loaders/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// const customConfig = {&lt;/span&gt;
&lt;span class="c1"&gt;//   resolve:{&lt;/span&gt;
&lt;span class="c1"&gt;//     alias: {&lt;/span&gt;
&lt;span class="c1"&gt;//       '@': path.resolve(__dirname, '..', '..', 'app/javascript/src')&lt;/span&gt;
&lt;span class="c1"&gt;//     }&lt;/span&gt;
&lt;span class="c1"&gt;//   }&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="c1"&gt;// environment.config.merge(customConfig)&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VueLoaderPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&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;VueLoaderPlugin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// module.exports = environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Include .vue files
&lt;/h3&gt;

&lt;p&gt;Next, open the file &lt;code&gt;config/webpacker.yml&lt;/code&gt; and add &lt;code&gt;.vue&lt;/code&gt; to the extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;//&lt;/span&gt; &lt;span class="c1"&gt;##############################&lt;/span&gt;
&lt;span class="s"&gt;// config/webpacker.yml&lt;/span&gt;
&lt;span class="s"&gt;//&lt;/span&gt; &lt;span class="c1"&gt;##############################&lt;/span&gt;

&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;


  &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.vue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Set Vue properties
&lt;/h3&gt;

&lt;p&gt;It is strongly recommended to properly configure some properties of Vue in order to get proper tree-shaking in the final bundle. You can find more information in &lt;a href="https://www.npmjs.com/package/vue/v/3.0.0" rel="noopener noreferrer"&gt;Vue3's README&lt;/a&gt; under &lt;code&gt;Bundler Build Feature Flags&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// webpack/environment.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;

&lt;span class="c1"&gt;// const { environment } = require('@rails/webpacker')&lt;/span&gt;
&lt;span class="c1"&gt;// const path = require('path')&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DefinePlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// const { VueLoaderPlugin } = require('vue-loader')&lt;/span&gt;
&lt;span class="c1"&gt;// const vue = require("./loaders/vue");&lt;/span&gt;

&lt;span class="c1"&gt;// const customConfig = {&lt;/span&gt;
&lt;span class="c1"&gt;//   resolve:{&lt;/span&gt;
&lt;span class="c1"&gt;//     alias: {&lt;/span&gt;
&lt;span class="c1"&gt;//       "@": path.resolve(__dirname, "..", "..", "app/javascript/src")&lt;/span&gt;
&lt;span class="c1"&gt;//     }&lt;/span&gt;
&lt;span class="c1"&gt;//   }&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="c1"&gt;// environment.config.merge(customConfig)&lt;/span&gt;

&lt;span class="c1"&gt;// environment.plugins.prepend(&lt;/span&gt;
&lt;span class="c1"&gt;//     'VueLoaderPlugin',&lt;/span&gt;
&lt;span class="c1"&gt;//     new VueLoaderPlugin()&lt;/span&gt;
&lt;span class="c1"&gt;// )&lt;/span&gt;

&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Define&lt;/span&gt;&lt;span class="dl"&gt;'&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;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;__VUE_OPTIONS_API__&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="c1"&gt;// or __VUE_OPTIONS_API__: true,&lt;/span&gt;
        &lt;span class="na"&gt;__VUE_PROD_DEVTOOLS__&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;// environment.loaders.prepend('vue', vue)&lt;/span&gt;

&lt;span class="c1"&gt;// module.exports = environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Create a Vue app
&lt;/h2&gt;

&lt;p&gt;Everything should now be correctly set up. It's finally time to add our first Single File Component and load it into a container.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Create entry point and SFC
&lt;/h3&gt;

&lt;p&gt;As explained above, I'd like to collect all Vue related code in a single directory. Therefore, create the folder &lt;code&gt;./app/javascript/src&lt;/code&gt; in your root directory. In there, create the file &lt;code&gt;main.js&lt;/code&gt;. It will be the entry point for the Vue application. Leave it empty as it is, we'll come back to it again.&lt;/p&gt;

&lt;p&gt;Next, let's create a Vue component. I propose to create the folder &lt;code&gt;./app/javascript/src/components&lt;/code&gt;. In there, create the file &lt;code&gt;HelloWorld.vue&lt;/code&gt;. You can name the file also &lt;code&gt;hello-world.vue&lt;/code&gt; if you prefer that syntax. Insert the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// ##############################
// app/javascript/src/components/HelloWorld.vue
// ##############################

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HelloWorld&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;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="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="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;blockquote&gt;
&lt;p&gt;Of course you can write sassy CSS. Therefore add the lang attribute &lt;code&gt;lang='scss'&lt;/code&gt; to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let's head back to our &lt;code&gt;main.js&lt;/code&gt; file and create the Vue app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// app/javascript/src/main.js&lt;/span&gt;
&lt;span class="c1"&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;createApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HelloWorld&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;@/components/HelloWorld.vue&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="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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HelloWorld&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#vue-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, let's recap what has happened. We created an SFC (Single File Component). We created a Vue instance and mounted it into an HTML element with the id &lt;code&gt;vue-app&lt;/code&gt;. But what, we haven't written this element yet. So let's do that now.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Create Home controller and view as root route
&lt;/h3&gt;

&lt;p&gt;For that, let's create a HomeController with a view. Alternatively, you can go directly to the &lt;code&gt;.erb&lt;/code&gt; file you want to add the Vue app to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails generate controller Home index 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, set the home controller as base route in &lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# -----------------&lt;/span&gt;
&lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;
&lt;span class="c1"&gt;# -----------------&lt;/span&gt;

&lt;span class="c1"&gt;# Rails.application.routes.draw do&lt;/span&gt;
&lt;span class="c1"&gt;#   get 'home/index'&lt;/span&gt;

  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'home#index'&lt;/span&gt;
&lt;span class="c1"&gt;# end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Connect Vue and Rails
&lt;/h3&gt;

&lt;p&gt;Finally, our configuration is done. Now we have a home page. We want to load the Vue app directly in this file. Head to &lt;code&gt;app/views/home/index.html&lt;/code&gt;. Add or replace the dummy content with the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/home/index.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'vue-app'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check out what's going on in the browser. Open your terminal and start the rails and the Webpack server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in one tab&lt;/span&gt;
rails server

&lt;span class="c"&gt;# in another tab&lt;/span&gt;
./bin/webpack-dev-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a browser and go to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;localhost:3000&lt;/a&gt;. If everything works, you should see nothing (but the dummy HTML code if you haven't removed it). When inspecting the DOM, there should be the empty &lt;code&gt;div container&lt;/code&gt; with the id &lt;code&gt;vue-app&lt;/code&gt;. Our last step to bring it all together is to import the entry point of our Vue application.&lt;/p&gt;

&lt;p&gt;To keep it simple, we will add the entry point directly to the &lt;code&gt;application.js&lt;/code&gt; in this tutorial. Of course, you can create a single pack for it. You can also use the split chunks feature of webpack(er). But for now, let's open &lt;code&gt;app/javascript/packs/application.js&lt;/code&gt; and import our entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ##############################&lt;/span&gt;
&lt;span class="c1"&gt;// app/javascript/packs/application.js&lt;/span&gt;
&lt;span class="c1"&gt;// ##############################&lt;/span&gt;

&lt;span class="c1"&gt;// require("@rails/ujs").start()&lt;/span&gt;
&lt;span class="c1"&gt;// require("turbolinks").start()&lt;/span&gt;
&lt;span class="c1"&gt;// require("@rails/activestorage").start()&lt;/span&gt;
&lt;span class="c1"&gt;// require("channels")&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;initVueApp&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;@/main.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;initVueApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload the page, you should see "Hello World" now! Have fun playing around with it for a while. Change the style, change the message, change the template. Don't forget, that you don't need to have a single root element in Vue3 anymore. Therefore no wrapping div container or similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; Next Steps
&lt;/h2&gt;

&lt;p&gt;Congratulations, you have just installed Vue3 in a Rails app. Next, we will talk about how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pass properties from Rails to Vue&lt;/li&gt;
&lt;li&gt;configure Linters&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/vannsl/webpacker-vue-3-and-typescript-1i99"&gt;configure TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow me to receive notifications, when I post those articles. I will link them here at the bottom of this article as soon as they're published.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@pixelatelier" rel="noopener noreferrer"&gt;Christian Holzinger&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/rails" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>vue3</category>
      <category>vue</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Tips for remote goodbyes</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Mon, 22 Jun 2020 07:36:41 +0000</pubDate>
      <link>https://dev.to/vannsl/tips-for-remote-goodbyes-2o15</link>
      <guid>https://dev.to/vannsl/tips-for-remote-goodbyes-2o15</guid>
      <description>&lt;p&gt;Depending on the period of notice you or your coworker leaves a company while most of us are working from home during the Corona situation. The past goodbye parties may have included ordering food and drinks, preparing snacks, ordering some pizza, having BBQ, cleaning the kitchen. How does that look like for goodbyes over video conferences? 40 people (or so) in a video chat, 6 of them talking? So, the question is: what could one do to have a nice goodbye experience remotely?&lt;/p&gt;

&lt;p&gt;Here are the key ideas of a nice remote goodbye evening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📅 Plan ahead
&lt;/li&gt;
&lt;li&gt;⏲️ Breaks
&lt;/li&gt;
&lt;li&gt;👗 Dress Code / Motto
&lt;/li&gt;
&lt;li&gt;🖼️ Slide Show
&lt;/li&gt;
&lt;li&gt;🎲 Game/Quiz
&lt;/li&gt;
&lt;li&gt;🥤 Several rooms
&lt;/li&gt;
&lt;li&gt;📸 Group Photo
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;📅 Plan ahead
&lt;/h2&gt;

&lt;p&gt;Since coworkers will have to buy their own snacks and drinks (unless you want to send all of them something ;) ) give them a chance to do so. Although there might be a lot of things left to do before leaving a company, don't forget about your party. Create a calendar appointment for one or two weeks beforehand. This makes it also easier for you to plan ahead further actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;⏲️ Breaks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dinner break
&lt;/h3&gt;

&lt;p&gt;A helpful gesture for coworkers can be to split up the event in two events. One directly at the end of the business day. For example 6 pm depending on the usual working hours of the company. This appointment can be used for goodbye speeches and giving everyone the opportunity to speak up. Afterward, all of the attendees can take a break after sitting in front of a monitor for most of the day. This gives time to prepare dinner, help with the kids' homework, and whatever needs to be done. The party crew can meet again later at 8 or 9 pm with refreshed eyes and nothing left to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small breaks
&lt;/h3&gt;

&lt;p&gt;In the following, we'll discuss some activities. They imply that the attendees will stay in front of the screen and won't be able to get a fresh beverage. Small breaks (5-10 minutes) that you announce at the beginning of the event can help with that. Everyone can stay focused on activities and leave the computer during breaks.&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%2Fi%2F5j3sj4p6w773e3njk2mu.jpg" 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%2Fi%2F5j3sj4p6w773e3njk2mu.jpg" alt="Fancy drinks" width="640" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;👗 Have a dress code and/or a motto party
&lt;/h2&gt;

&lt;p&gt;One possibility to increase the anticipation is to come up with a dress code or a motto for the party. Being dressed in the same color looks also nice in a tile layout of video conference tools. &lt;/p&gt;

&lt;p&gt;A motto party idea can include everyone using a beach image or filter for the background but I would leave that decision to the individual people.&lt;/p&gt;

&lt;p&gt;Another idea is to set up a list of specific food or drinks everybody can enjoy together remotely. This includes a bit of planning but should be feasible. Some ideas are Gin Tonic Friday, Vegetable Chips Thursday, or Wine Wednesday. But it should be made clear that non-alcoholic drinks, especially on evenings before a workday are a good, responsible and reasonable choice :)&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;🖼️ Slide Show
&lt;/h2&gt;

&lt;p&gt;It's always nice to remember the past years working together by enjoying a photo show together. Here's the chance to get creative. Use a tool of your choice, e.g. &lt;a href="https://www.apple.com/de/imovie/" rel="noopener noreferrer"&gt;iMovie&lt;/a&gt; or &lt;a href="https://www.microsoft.com/de-de/microsoft-365/powerpoint" rel="noopener noreferrer"&gt;PowerPoint&lt;/a&gt; to gather all of the photos.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;🎲 Game/Quiz Time
&lt;/h2&gt;

&lt;p&gt;To involve everyone in the room, it's time to play a game. The options are limitless. Maybe you already had a game you've always played in the office and find an online solution for that. To get a personal touch into the game, you can create a quiz (e.g. using &lt;a href="https://kahoot.com" rel="noopener noreferrer"&gt;kahoot&lt;/a&gt;) including questions on which projects you've been working on, what the name of your kids is of whatever you'd like to tell people.&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%2Fi%2Fmnunwgedq3epulkbd8xi.jpg" 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%2Fi%2Fmnunwgedq3epulkbd8xi.jpg" alt="Colleagues looking at monitor, laughing" width="640" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;🥤 Prepare rooms
&lt;/h2&gt;

&lt;p&gt;Since there are already some events going on in the main video chat (e.g. a &lt;a&gt;&lt;/a&gt;Slide Show or a &lt;a&gt;&lt;/a&gt;Quiz), people won't be able to have meaningful conversations outside the breaks. Depending on how many attendees there are, it could make sense to prepare several video chat instances for people to split up. A nice side effect is that you can give those rooms the names of the original rooms of the office. Ideas are "kitchen" and "balcony" or special meeting room names you might have at your place.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;📸 Group Photo
&lt;/h2&gt;

&lt;p&gt;Not being in the same room does not imply that there can't be photos made together. Just that these photos ar screenshots. There is only one very important rule: Ask all of the attendees if it's okay to make that screenshot. If some are not, shutting off the camera or leaving the video call for half a minute are possible fallbacks.&lt;/p&gt;

&lt;p&gt;(If Google Meet is used, there is a helpful browser extension called &lt;a href="https://chrome.google.com/webstore/detail/google-meet-grid-view/kklailfgofogmmdlhgmjgenehkjoioip" rel="noopener noreferrer"&gt;Grid View&lt;/a&gt; to see more tiles than usual on the screen)&lt;/p&gt;

&lt;p&gt;🎉🥳🎊 &lt;strong&gt;Have a great party&lt;/strong&gt; 🎉🥳🎊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc7cg1oxgv4koaskejnxt.jpg" 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%2Fi%2Fc7cg1oxgv4koaskejnxt.jpg" alt="Group photo" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Photos of this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cover image: Photo by &lt;a href="https://unsplash.com/@craft_ear?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Erik Mclean&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/goodbye?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Breaks: Photo by &lt;a href="https://unsplash.com/@heftiba?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Toa Heftiba&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/white-party?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Game: Photo by &lt;a href="https://unsplash.com/@windows?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Windows&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/colleagues?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Photo: Photo by &lt;a href="https://unsplash.com/@miinyuii?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Duy Pham&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/group?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>remote</category>
      <category>wfh</category>
      <category>homeoffice</category>
    </item>
    <item>
      <title>Playwright (vs. Puppeteer): Cross-Browser Testing done right</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Sun, 24 May 2020 15:08:13 +0000</pubDate>
      <link>https://dev.to/vannsl/playwright-vs-puppeteer-cross-browser-testing-done-right-4e3</link>
      <guid>https://dev.to/vannsl/playwright-vs-puppeteer-cross-browser-testing-done-right-4e3</guid>
      <description>&lt;p&gt;Playwright version: v1.0.2&lt;/p&gt;

&lt;p&gt;The ecosystem of Frontend Development changes rapidly. This is a curse and a blessing at the same time. The choice of tools frontend developers grows, too. Before reading the whole documentation of a new tool, install it, and start writing testing code, frontend developers might want to make sure it is worth the time and effort before.&lt;/p&gt;

&lt;p&gt;So what is &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;? Is it an additional testing tool? Does it replace a known framework like Jest or Cypress? Let's dive right into these questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is Playwright?&lt;/li&gt;
&lt;li&gt;Which role does Playwright play?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;What is Playwright?
&lt;/h2&gt;

&lt;p&gt;Playwright is an automated UI testing tool. It's a Node.js library that enables developers to run E2E tests in modern browsers: Chromium, Firefox, WebKit, and even Microsoft Edge.   &lt;/p&gt;

&lt;p&gt;The enablement of those major browsers is one of the key features of Playwright. It is a powerful argument to prefer this tool above others. But there is more. Frontend developers need to test their code not only in different browsers but on various devices, eventually with offline modes of Progressive Webapps.&lt;/p&gt;

&lt;p&gt;Have a look at the &lt;a href="https://playwright.dev/#version=v1.0.2&amp;amp;path=docs%2Fcore-concepts.md&amp;amp;q=" rel="noopener noreferrer"&gt;core concepts of Playwright&lt;/a&gt; to get a feeling for the developer experience. The &lt;a href="https://playwright.dev/#version=v1.0.2&amp;amp;path=docs%2Fintro.md&amp;amp;q=installation" rel="noopener noreferrer"&gt;installation of the testing library&lt;/a&gt; is made as simple as possible. After the execution of the install script everything including browser settings is already setup. It is made simple to configure, install, or skip different versions of browsers. &lt;a href="https://github.com/microsoft/playwright/blob/master/docs/api.md" rel="noopener noreferrer"&gt;Playwright's API&lt;/a&gt; is testing-friendly. During the execution of tests, developers can interrupt them with the Developer Tools of the browsers or using the Microsoft VS Code Debugging Tools. &lt;/p&gt;

&lt;p&gt;I won't get into the details on how to install and use Playwright. This content could get outdated in the future. Check out the &lt;a href="https://playwright.dev/#version=v1.0.2&amp;amp;path=docs%2Fintro.md&amp;amp;q=" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; to explore the testing tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Which role does Playwright play?
&lt;/h2&gt;

&lt;p&gt;Let's have a very short recap. Playwright is a new library for browser automation. &lt;a href="https://www.selenium.dev/" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; came out in 2004 and was the most used tool for browser automation tests for a relatively long time. &lt;a href="https://phantomjs.org/" rel="noopener noreferrer"&gt;PhantomJS&lt;/a&gt; helped to render the UI during tests. Google released the &lt;a href="https://github.com/puppeteer/puppeteer/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;first major version of Puppeteer&lt;/a&gt; in January 2018. Two years later in January 2020 Microsoft announced Playwright.&lt;/p&gt;

&lt;p&gt;It gets interesting when comparing who is contributing to &lt;a href="https://github.com/puppeteer/puppeteer/graphs/contributors" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; and who is contributing to &lt;a href="https://github.com/microsoft/playwright/graphs/contributors" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;. The most active contributors of Puppeteer &lt;a href="https://github.com/aslushnikov" rel="noopener noreferrer"&gt;Andrey Lushnikov&lt;/a&gt; and &lt;a href="https://github.com/JoelEinbinder" rel="noopener noreferrer"&gt;Joel Einbinder&lt;/a&gt; are now part of the team of Playwright. In one version of the repository's &lt;a href="https://github.com/microsoft/playwright/pull/711/files" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; they stated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We are the same team that originally built Puppeteer at Google but has since then moved on. Puppeteer proved that there is a lot of interest in the new generation of ever-green, capable, and reliable automation drivers. With Playwright, we'd like to take it one step further and offer the same functionality for &lt;strong&gt;all&lt;/strong&gt; the popular rendering engines. We'd like to see Playwright vendor-neutral and shared governed.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;Playwright &lt;a href="https://github.com/microsoft/playwright/commit/53cdbc5688935810b8c51ec86e3037c24bbcfac1#diff-04c6e90faac2675aa89e2176d2eec7d8" rel="noopener noreferrer"&gt;README.md&lt;/a&gt;: Q: How does Playwright relate to &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;?&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Playwright has all of the features that Puppeteer has, too. The team took advantage of the lessons they have learned during the development of Puppeteer.&lt;/p&gt;

&lt;p&gt;One might ask why the team has not decided to improve Puppeteer with those features but create a new framework. Apart from the browser support, what are the differences between Puppeteer and Playwright? Being cloud-native, supporting a BrowserContext for multi-page scenarios, the setting of the user-agent and device emulations are some additional features.&lt;/p&gt;

&lt;p&gt;Those features would have led to breaking changes in the Puppeteer API. But testing code in Chromium only might be enough for some codebases. Internal user interfaces for dashboards or Admin Areas don't have to support all of the different browsers or work offline.&lt;/p&gt;

&lt;p&gt;The team decided to create a new framework instead. That does not imply that a switch from Puppeteer to Playwright would be a never-ending task. The API and core concept are currently still similar and it should be possible to migrate from Puppeteer to Playwright. If you have any experience, leave a comment!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR: Playwright is the successor of Puppeteer including support for all major browsers.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>testing</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Configuring TailwindCSS for Sapper</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Fri, 31 Jan 2020 06:46:05 +0000</pubDate>
      <link>https://dev.to/vannsl/configuring-tailwindcss-for-sapper-58jm</link>
      <guid>https://dev.to/vannsl/configuring-tailwindcss-for-sapper-58jm</guid>
      <description>&lt;p&gt;This article is Part III of my first three posts about Svelte. Part I described &lt;a href="https://dev.to/vannsl/statically-generated-website-with-svelte-and-sapper-5bi7"&gt;how to create Statically generated website with Svelte and Sapper&lt;/a&gt;. Part II discusses &lt;a href="https://dev.to/vannsl/all-you-need-to-know-to-start-writing-svelte-single-file-components-cbd"&gt;Svelte Single File Components&lt;/a&gt; in more detail.&lt;/p&gt;

&lt;p&gt;In this article we will configure &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt; for &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; + &lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Versions:&lt;/strong&gt;&lt;br&gt;
Svelte: 3.18.0&lt;br&gt;
Sapper: 0.27.9 (! early state)&lt;br&gt;
TailwindCSS: 1.1.4&lt;br&gt;
@fullhuman/postcss-purgecss: 1.3.0&lt;br&gt;
PostCSS: 7.0.26&lt;br&gt;
PostCSS-CLI: 7.1.0&lt;br&gt;
PostCSS-Load-Config: 2.1.0&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I forked the official &lt;a href="https://www.google.com/search?client=firefox-b-d&amp;amp;q=sapper+template" rel="noopener noreferrer"&gt;sapper-template&lt;/a&gt; repository. It includes the integration of TailwindCSS, PostCSS and PurgeCSS. You can install the &lt;a href="https://github.com/Vannsl/sapper-tailwindcss-template" rel="noopener noreferrer"&gt;sapper-tailwindcss-template&lt;/a&gt; repository. Then you don't have to go through the Step by Step Guide below. To use it, execute the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx degit &lt;span class="s2"&gt;"vannsl/sapper-tailwindcss-template#rollup"&lt;/span&gt; sapper-tailwindcss-template
&lt;span class="c"&gt;# or&lt;/span&gt;
npx degit &lt;span class="s2"&gt;"vannsl/sapper-tailwindcss-template#webpack"&lt;/span&gt; sapper-tailwindcss-template
&lt;span class="nb"&gt;cd &lt;/span&gt;sapper-tailwindcss-template
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To start the local server and watch tailwind, execute these two commands in &lt;strong&gt;separated&lt;/strong&gt; windows of your terminal:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev:tailwindcss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Vannsl" rel="noopener noreferrer"&gt;
        Vannsl
      &lt;/a&gt; / &lt;a href="https://github.com/Vannsl/sapper-tailwindcss-template" rel="noopener noreferrer"&gt;
        sapper-tailwindcss-template
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Starter template for Sapper apps
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;sapper-tailwindcss-template&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;The is a fork of the default &lt;a href="https://github.com/sveltejs/sapper" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt; template, available for Rollup and webpack. It extends the default template by installing &lt;a href="https://tailwindcss.com/" rel="nofollow noopener noreferrer"&gt;TailwindCSS&lt;/a&gt;, &lt;a href="https://postcss.org/" rel="nofollow noopener noreferrer"&gt;PostCSS&lt;/a&gt; and &lt;a href="https://github.com/FullHuman/purgecss" rel="noopener noreferrer"&gt;PurgeCSS&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting started&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Using &lt;code&gt;degit&lt;/code&gt;
&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/Rich-Harris/degit" rel="noopener noreferrer"&gt;&lt;code&gt;degit&lt;/code&gt;&lt;/a&gt; is a scaffolding tool that lets you create a directory from a branch in a repository. Use either the &lt;code&gt;rollup&lt;/code&gt; or &lt;code&gt;webpack&lt;/code&gt; branch in &lt;code&gt;sapper-template&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; for Rollup&lt;/span&gt;
npx degit &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;vannsl/sapper-tailwindcss-template#rollup&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; my-app
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; for webpack&lt;/span&gt;
npx degit &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;vannsl/sapper-tailwindcss-template#webpack&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; my-app&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Using GitHub templates&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;Alternatively, you can use GitHub's template feature with the &lt;a href="https://github.com/sveltejs/sapper-template-rollup" rel="noopener noreferrer"&gt;sapper-template-rollup&lt;/a&gt; or &lt;a href="https://github.com/sveltejs/sapper-template-webpack" rel="noopener noreferrer"&gt;sapper-template-webpack&lt;/a&gt; repositories.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Running the project&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;However you get the code, you can install dependencies and run the project in development mode with:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; my-app
npm install &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or yarn&lt;/span&gt;
npm run dev&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open up &lt;a href="http://localhost:3000" rel="nofollow noopener noreferrer"&gt;localhost:3000&lt;/a&gt; and start clicking around.&lt;/p&gt;
&lt;p&gt;Consult &lt;a href="https://sapper.svelte.dev" rel="nofollow noopener noreferrer"&gt;sapper.svelte.dev&lt;/a&gt; for help getting started.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Structure&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Sapper expects to find two directories in the root of your project —…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Vannsl/sapper-tailwindcss-template" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Existing methods
&lt;/h2&gt;

&lt;p&gt;On Github, there already exists a &lt;a href="https://github.com/tailwindcss/setup-examples/tree/master/examples/sapper" rel="noopener noreferrer"&gt;TailwindCSS Setup Example for Sapper&lt;/a&gt;. Although the general setup works, I had problems with &lt;a href="https://purgecss.com/" rel="noopener noreferrer"&gt;PurgeCSS&lt;/a&gt;. The not used CSS of the TailwindCSS Framework was not removed when &lt;strong&gt;exporting&lt;/strong&gt; a static version of my Sapper application. Maybe I did something wrong.&lt;/p&gt;

&lt;p&gt;I did a bit of research and after a few try and error approaches, I finally got it to work. Here's the Step by Step Guide:&lt;/p&gt;

&lt;h2&gt;
  
  
  Step by Step Guide
&lt;/h2&gt;

&lt;p&gt;In the following, we'll install Sapper and TailwindCSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Sapper app
&lt;/h3&gt;

&lt;p&gt;The following commands will install the example project for Sapper using the &lt;a href="https://rollupjs.org/guide/en/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; Configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx degit &lt;span class="s2"&gt;"sveltejs/sapper-template#rollup"&lt;/span&gt; sapper-tailwindcss
&lt;span class="c"&gt;# or&lt;/span&gt;
npx degit &lt;span class="s2"&gt;"sveltejs/sapper-template#webpack"&lt;/span&gt; sapper-tailwindcss
&lt;span class="nb"&gt;cd &lt;/span&gt;sapper-tailwindcss
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your Sapper application is installed. Run &lt;code&gt;npm run dev&lt;/code&gt; to start the local server. Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to check out the example project.&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%2Fi%2Fk3ym9ozmybuk5fg5owmy.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%2Fi%2Fk3ym9ozmybuk5fg5owmy.png" alt="Alt Text" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Download TailwindCSS, PostCSS and PurgeCSS
&lt;/h3&gt;

&lt;p&gt;The following commands will download the packages for &lt;a href="https://postcss.org/" rel="noopener noreferrer"&gt;PostCSS&lt;/a&gt; and &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt; as &lt;a href="https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file" rel="noopener noreferrer"&gt;devDependencies&lt;/a&gt; and &lt;a href="https://purgecss.com/" rel="noopener noreferrer"&gt;PurgeCSS&lt;/a&gt; as a dependency:&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; &lt;span class="nt"&gt;-D&lt;/span&gt; postcss-cli tailwindcss &lt;span class="nt"&gt;--save&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @fullhuman/postcss-purgecss &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Configurations
&lt;/h3&gt;

&lt;p&gt;The order of the following steps is not important. It will only work when all of the following changes are implemented.&lt;/p&gt;

&lt;h3&gt;
  
  
  tailwind.config.js
&lt;/h3&gt;

&lt;p&gt;Afterward, initialize TailwindCSS with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tailwind init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates the file &lt;code&gt;tailwind.config.js&lt;/code&gt; in the root directory of your project. It contains the following skeleton:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on how to customize TailwindCSS, please read the &lt;a href="https://tailwindcss.com/docs/configuration" rel="noopener noreferrer"&gt;official TailwindCSS configuration documentation&lt;/a&gt;. You can leave it for now as it is.&lt;/p&gt;

&lt;h3&gt;
  
  
  postcss.config.js
&lt;/h3&gt;

&lt;p&gt;Create an empty file with the name &lt;code&gt;postcss.config.js&lt;/code&gt;. Either by creating this file in your IDE or finder or by executing the following command (if macOS) in the root folder of the sapper application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;postcss.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterward, append the following content to the file:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tailwindcss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;purgecss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fullhuman/postcss-purgecss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;defaultExtractor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z0-9-_:&lt;/span&gt;&lt;span class="se"&gt;/]&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./tailwind.config.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="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;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;purgecss&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  rollup.config.js/webpack.config.js
&lt;/h3&gt;

&lt;p&gt;Nothing to do. I added that section here because other approaches include adding PostCSS to the rollup config. There is no need to do this when using the approach of this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  static/tailwind.css
&lt;/h3&gt;

&lt;p&gt;Now it's time to add the TailwindCSS Styles to your project. Create a new CSS file in the statics directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;static
&lt;span class="nb"&gt;touch &lt;/span&gt;tailwind.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To import the TailwindCSS Styles, the rules have to be applied in this file:&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="c"&gt;/* static/tailwind.css */&lt;/span&gt;

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name of the CSS file is not important. It's best practice to use names like &lt;code&gt;tailwind.css&lt;/code&gt;, &lt;code&gt;main.css&lt;/code&gt; or &lt;code&gt;global.css&lt;/code&gt;. Since the skeleton project of Sapper already provides a &lt;code&gt;global.css&lt;/code&gt;, this tutorial recommends to use the name &lt;code&gt;tailwind.css&lt;/code&gt; to prevent conflicts. When using a Utility-Based CSS Framework the styles of the preconfigured &lt;code&gt;global.css&lt;/code&gt; may not be needed. If you want to, you can also use this file and override the default settings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This file can be used either for only adding TailwindCSS to the Sapper project or be extended with further global rules and styles. For example, custom CSS classes or &lt;a href="https://developer.mozilla.org/de/docs/Web/CSS/@font-face" rel="noopener noreferrer"&gt;Font Faces&lt;/a&gt; can be registered here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  package.json
&lt;/h3&gt;

&lt;p&gt;Mostly done. To get the TailwindCSS CSS into the built application for dev and production mode, the following npm scripts in the &lt;code&gt;package.json&lt;/code&gt; have to be added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// package.json&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev:tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postcss static/tailwind.css -o static/main.css -w&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build:tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NODE_ENV=production postcss static/tailwind.css -o static/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;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will create (or override if already existing) the file &lt;code&gt;main.css&lt;/code&gt; in the &lt;code&gt;static&lt;/code&gt; folder. The first command &lt;code&gt;dev:tailwindcss&lt;/code&gt; will create a CSS file including all of the TailwindCSS styles. Any change in your source code will be immediately applied to the website with hot module replacement. When executing PostCSS in the &lt;code&gt;production&lt;/code&gt; environment with &lt;code&gt;NODE_ENV=production&lt;/code&gt; the file &lt;code&gt;main.css&lt;/code&gt; will include only the styles of TailwindCSS you're using in your application thanks to PurgeCSS. If you try out both versions, you should see a significant difference in the file size of &lt;code&gt;main.css&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You don't need to name that file &lt;code&gt;main.css&lt;/code&gt;. You can choose any name that's not taken yet in your project. In the next section, we'll import the built CSS file in our application. But first, we'll add the statement to execute &lt;code&gt;build:tailwindcss&lt;/code&gt; when building or exporting the Sapper application. Therefore add &lt;code&gt;npm run build:tailwindcss &amp;amp;&amp;amp;&lt;/code&gt; at the beginning of the &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// package.json for rollup&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="c1"&gt;// "dev:tailwindcss": "postcss static/tailwind.css -o static/main.css -w",&lt;/span&gt;
 &lt;span class="c1"&gt;// "build:tailwindcss": "NODE_ENV=production postcss static/tailwind.css -o static/main.css",&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npm run build:tailwindcss &amp;amp;&amp;amp; sapper build --legacy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;export&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npm run build:tailwindcss &amp;amp;&amp;amp; sapper export --legacy&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;span class="c1"&gt;// ...&lt;/span&gt;


&lt;span class="c1"&gt;// package.json for webpack&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nl"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="c1"&gt;// "dev:tailwindcss": "postcss static/tailwind.css -o static/main.css -w",&lt;/span&gt;
 &lt;span class="c1"&gt;// "build:tailwindcss": "NODE_ENV=production postcss static/tailwind.css -o static/main.css",&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npm run build:tailwindcss &amp;amp;&amp;amp; sapper build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;export&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npm run build:tailwindcss &amp;amp;&amp;amp; sapper export&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;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .gitignore
&lt;/h3&gt;

&lt;p&gt;If you've initialized a git repository, I recommend to add &lt;code&gt;/static/main.css&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; file. For example, that's how the &lt;code&gt;.gitignore&lt;/code&gt; of the Demo Git Repository looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.DS_Store
/node_modules/
/src/node_modules/@sapper/
yarn-error.log
/cypress/screenshots/
/__sapper__/
/static/main.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  src/template.html
&lt;/h3&gt;

&lt;p&gt;To import the generated &lt;code&gt;main.css&lt;/code&gt; file, add the following line in the file &lt;code&gt;src/template.html&lt;/code&gt; just above the other import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/template.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"main.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the project
&lt;/h3&gt;

&lt;p&gt;To run the application in production more, execute &lt;code&gt;npm run build&lt;/code&gt;. To generate the static site of the application, run &lt;code&gt;npm run export&lt;/code&gt;. By adding &lt;code&gt;npm run build:tailwindcss&lt;/code&gt; to these scripts in the &lt;code&gt;package.json&lt;/code&gt;, the commands will automatically generate the file &lt;code&gt;main.css&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To run the project locally, execute the following commands in &lt;strong&gt;separated&lt;/strong&gt; windows of your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev:tailwindcss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👏 That's it. You're done. 👏&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>svelte</category>
      <category>tailwindcss</category>
      <category>sapper</category>
    </item>
    <item>
      <title>All you need to know to start writing Svelte Single File Components</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Fri, 17 Jan 2020 05:41:22 +0000</pubDate>
      <link>https://dev.to/vannsl/all-you-need-to-know-to-start-writing-svelte-single-file-components-cbd</link>
      <guid>https://dev.to/vannsl/all-you-need-to-know-to-start-writing-svelte-single-file-components-cbd</guid>
      <description>&lt;p&gt;This article is Part II of my first three posts about Svelte. In my opinion, the principle to have a JavaScript &lt;a href="https://svelte.dev/blog/frameworks-without-the-framework" rel="noopener noreferrer"&gt;(UI) framework without a framework&lt;/a&gt; is the most promising for the future of frontend development. I'm looking forward writing about Svelte. Part I discusses how to create a &lt;a href="https://dev.to/vannsl/statically-generated-website-with-svelte-and-sapper-5bi7"&gt;statically generated website with Svelte and Sapper&lt;/a&gt;. Part III shows &lt;a href="https://dev.to/vannsl/configuring-tailwindcss-for-sapper-58jm"&gt;how to use TailwindCSS with Svelte and Sapper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Version:&lt;/em&gt;&lt;br&gt;
Svelte: 3.16.7&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Svelte is a modern framework to create cybernetically enhanced web apps. Without loading the whole library on the client, it does all of the work in the compile step. There is a lot more to tell about Svelte, but today we'll only talk about how to create things with Svelte.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This article focuses on writing Svelte Single File Components.&lt;/strong&gt; It describes how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
structure a svelte file

&lt;ul&gt;
&lt;li&gt;create data attributes&lt;/li&gt;
&lt;li&gt;render markup conditionally&lt;/li&gt;
&lt;li&gt;handle events&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
compose components

&lt;ul&gt;
&lt;li&gt;pass properties to child components&lt;/li&gt;
&lt;li&gt;use slots&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;continue with Svelte&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A .svelte Single File Component (SFC)
&lt;/h2&gt;

&lt;p&gt;Svelte SFCs look very similar to Vue or with other words: like HTML, JavaScript, and CSS. The usual differs slightly from Vue SFCs:&lt;/p&gt;

&lt;p&gt;A .svelte SFC starts with the &lt;strong&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block&lt;/strong&gt;. It contains the logic of the SFC. It can import child components and export data attributes and properties.&lt;/p&gt;

&lt;p&gt;It is followed by the &lt;strong&gt;&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block&lt;/strong&gt;. It contains the CSS for this component. Compared to other component-based frameworks, the style it automatically &lt;code&gt;scoped&lt;/code&gt; to this component without adding a keyword for it. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Styles are scoped to the component by default. For global styles add the keyword: &lt;code&gt;&amp;lt;style global&amp;gt;&amp;lt;/style&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The part of the HTML is surprisingly not wrapped inside in a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; block. There is also no need to wrap all of the HTML tags within one surrounding tag. It's important: there is &lt;strong&gt;no &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; block&lt;/strong&gt;. HTML can just be added to the file.&lt;/p&gt;

&lt;p&gt;An example .svelte SFC looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- component.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// nothing to do here. This block can be removed.&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello World!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;I am a Demo.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By not wrapping the HTML in surrounding tags, there are a few things different from other component-based frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grid styling might get simpler by having more freedom&lt;/li&gt;
&lt;li&gt;besides passing properties, it is not possible to add other attributes like &lt;code&gt;class=""&lt;/code&gt; to a child component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Data attributes
&lt;/h3&gt;

&lt;p&gt;The template language of Svelte is Angular/Vue alike. Like in those frameworks, there are similarities to &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;Handlebars&lt;/a&gt;. To use variables within the template, wrap them in curly braces: &lt;code&gt;{VARIABLE}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello {title}!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;I am a Demo.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Although I would consider it as a bad idea to add HTML via variables in the markup, it's possible by writing &lt;code&gt;&amp;lt;h1&amp;gt;Hello {@html title}&amp;lt;/h1&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Variables can also be used to bind dynamic values to HTML attributes. E.g. an anchor link with a href variable looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;{href}&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Open Link
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A common mistake at the beginning when using variables for HTML attributes could be to add quotes for the variables. It's &lt;code&gt;href={href}&lt;/code&gt; and not &lt;code&gt;href="{href}"&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A nice feature is to use &lt;a href="https://svelte.dev/tutorial/dynamic-attributes" rel="noopener noreferrer"&gt;shorthand attributes&lt;/a&gt;. When the name of the variable equals the name of the attribute, it's enough to add only the attribute in curly braces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Open Link
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Shorthand attributes can reduce redundant code. For example, it's possible to use &lt;code&gt;{href}&lt;/code&gt; for &lt;code&gt;href={href}&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's even possible to spread an object to add several attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noopener&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="err"&gt;{...&lt;/span&gt;&lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- will be compiled to: &amp;lt;a href="some src" target="_blank" rel="noopener"&amp;gt;--&amp;gt;&lt;/span&gt;
  Open Link
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional rendering
&lt;/h3&gt;

&lt;p&gt;Compared to other frameworks, &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; blocks are not implemented with directives. For &lt;a href="https://vuejs.org/v2/guide/conditional.html" rel="noopener noreferrer"&gt;conditional rendering in Vue&lt;/a&gt; one would write: &lt;code&gt;&amp;lt;p v-if="true"&amp;gt;&lt;/code&gt;. For &lt;a href="https://svelte.dev/docs#if" rel="noopener noreferrer"&gt;conditional rendering in Svelte&lt;/a&gt; one can achieve that by adding pure &lt;code&gt;if&lt;/code&gt; blocks, wrapped in curly braces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{#if Math.random() &amp;gt; 0.5}
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;I like Svelte.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
{:else}
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;I don't like Svelte.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For conditional rendering, &lt;code&gt;if-else&lt;/code&gt; blocks can be written in curly braces. &lt;code&gt;Else&lt;/code&gt; blocks are optional. &lt;code&gt;#&lt;/code&gt; opens a block, &lt;code&gt;:&lt;/code&gt; indicates a block continuation tag and &lt;code&gt;/&lt;/code&gt; closes the block.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Event Handler
&lt;/h3&gt;

&lt;p&gt;Before the era of UI frameworks, developers used Vanilla JavaScript to add logic and behavior onto websites. &lt;a href="https://www.w3schools.com/jsref/event_onclick.asp" rel="noopener noreferrer"&gt;Onclick&lt;/a&gt; handler provided the functionality to add callbacks to HTML elements, as soon as users clicked on them. In Svelte, event handlers are added to DOM elements by using the &lt;a href="https://svelte.dev/docs#on_element_event" rel="noopener noreferrer"&gt;element directive &lt;code&gt;on:&lt;/code&gt;&lt;/a&gt;. It's possible to either pass a function as reference or write an inline function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;on:&lt;/code&gt; to add event handlers on DOM elements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few examples to get a feeling for event handlers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... log user out&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toggleTooltip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... show or hide tooltip&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{logout}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Logout
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;on:mouseover=&lt;/span&gt;&lt;span class="s"&gt;{toggleTooltip}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Info
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;"{() =&amp;gt; a += 1}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- you can pass the event as property: --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- &amp;lt;button on:click|preventDefault="{(e) =&amp;gt; /* do something with the event e */}"&amp;gt; --&amp;gt;&lt;/span&gt;
  a is {a}.
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's talk about how to compose components next.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Composing Components
&lt;/h2&gt;

&lt;p&gt;Child Components can be imported in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block. They don't have to be registered any further.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GridItem&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;../components/GridItem.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component can be added to the template like other HTML tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;GridItem&amp;gt;&amp;lt;/GridItem&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- or &amp;lt;GridItem /&amp;gt; --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Child components are written in CamelCase and imported in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The names of the components are case sensitive. It is recommended to use PascalCase for the names. The advantage of that is that it is possible to use &lt;em&gt;Header&lt;/em&gt;, &lt;em&gt;Footer&lt;/em&gt; and other already taken tag names as names for the Svelte components. This is different from other frameworks where names like &lt;em&gt;TheHeader&lt;/em&gt;  or &lt;em&gt;TheFooter&lt;/em&gt; are a workaround, although also making clear that these components should also only be used once within the page template.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Passing properties
&lt;/h3&gt;

&lt;p&gt;Properties can be passed with curly braces &lt;code&gt;{}&lt;/code&gt;to the child components. Children can access the properties by exporting them. While to declare data attributes the syntax is &lt;code&gt;[let/const] variable = 'abc';&lt;/code&gt;, the syntax for accessing passed properties is &lt;code&gt;export let variable;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Parent.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Child&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;../components/Child.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;Child&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"World!"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/Child&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Child.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello {title}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An optional &lt;strong&gt;default value&lt;/strong&gt; can be added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Child.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my dear friend.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello {title}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;&amp;lt;Child&amp;gt;&amp;lt;/Child&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;Child /&amp;gt;&lt;/code&gt; for nesting components without using slots.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Slots
&lt;/h3&gt;

&lt;p&gt;Now, it is time to create a simple Grid with GridItems slots that can be added by using the tag &lt;code&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/code&gt;. Please be aware of the simplicity of the implementation of this grid, which only allows two columns per row. &lt;a href="https://github.com/vaheqelyan" rel="noopener noreferrer"&gt;@vaheqelyan&lt;/a&gt; implemented a much more complex version of a &lt;a href="https://svelte-grid.now.sh/" rel="noopener noreferrer"&gt;Svelte-Grid&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Grid.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use several slots within one component, append the attribute &lt;code&gt;name=&lt;/code&gt; to create &lt;strong&gt;named slots&lt;/strong&gt;. Named slots and unnamed slots can be combined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- GridItem.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- or &amp;lt;slot /&amp;gt; --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is a powerful way to create complex compositions of components. In the following we'll see a whole Grid example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- index.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Grid&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;../components/Grid.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GridItem&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;../components/GridItem.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World!";
&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello {title}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;GridItem&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I will be rendered within the named slot title.&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I will be rendered within the named slot.&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;I will be rendered within the unnamed slot.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/GridItem&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;GridItem&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I only have a headline, nothing else.&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/GridItem&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To add components, data, properties, etc. in Svelte, they only have to be imported or declared in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block. There is no need to register them any further.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;🤩 Prepared with this knowledge, implement your first basic Svelte Single File Component 🤩&lt;/p&gt;

&lt;p&gt;Small static pages often don't need more functionality. To strengthen the knowledge, I recommend using 1h to go through the official &lt;a href="https://svelte.dev/tutorial/basics" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is more to learn. Svelte offers &lt;a href="https://svelte.dev/tutorial/onmount" rel="noopener noreferrer"&gt;lifecycle hooks&lt;/a&gt;, &lt;a href="https://svelte.dev/tutorial/text-inputs" rel="noopener noreferrer"&gt;bindings&lt;/a&gt;, &lt;a href="https://svelte.dev/tutorial/writable-stores" rel="noopener noreferrer"&gt;stores&lt;/a&gt;, &lt;a href="https://svelte.dev/tutorial/transition" rel="noopener noreferrer"&gt;transitions&lt;/a&gt; and much more to create more complex applications. Keep on learning 👨‍🎓👩‍🎓&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>svelte</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Statically generated website with Svelte and Sapper</title>
      <dc:creator>Vanessa Otto</dc:creator>
      <pubDate>Sun, 05 Jan 2020 10:51:52 +0000</pubDate>
      <link>https://dev.to/vannsl/statically-generated-website-with-svelte-and-sapper-5bi7</link>
      <guid>https://dev.to/vannsl/statically-generated-website-with-svelte-and-sapper-5bi7</guid>
      <description>&lt;p&gt;This article is Part I of my first three posts about Svelte. Part II discusses &lt;a href="https://dev.to/vannsl/all-you-need-to-know-to-start-writing-svelte-single-file-components-cbd"&gt;Svelte Single File Components&lt;/a&gt; in more detail. Part III shows &lt;a href="https://dev.to/vannsl/configuring-tailwindcss-for-sapper-58jm"&gt;how to use TailwindCSS with Svelte and Sapper&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In this article we will configure VS Code for Svelte, install the demo application of Sapper, discuss the project structure, run the E2E tests, build the demo application statically and deploy it on Netlify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Versions:&lt;/strong&gt;&lt;br&gt;
Svelte: 3.16.7&lt;br&gt;
Sapper: 0.27.9 (!  early state)&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will go through the process of setting up a statically generated website with &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; or more specifically its companion framework &lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt;. I'm currently using this framework to build my portfolio web page from scratch. The code of currently unfinished version can be found on &lt;a href="https://github.com/Vannsl/portfolio-sapper" rel="noopener noreferrer"&gt;my Sapper repository on github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Svelte?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; is a free and open-source software for creating performant web sites. In comparison to &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;, Svelte does a lot of work already in the compile step while building the application. React and Vue do a lot of these tasks in the browser. I've tried it out and in my opinion. It's worth to give it a shot or at least keep it in mind.&lt;/p&gt;

&lt;p&gt;I won't go further into the technical details of Svelte. The contributors know them better as me. There is a lot of great material on their &lt;a href="https://svelte.dev/blog" rel="noopener noreferrer"&gt;blog&lt;/a&gt; to understand how the framework works. They provide a detailed &lt;a href="https://svelte.dev/tutorial/basics" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt;. The talk of Rich Harris, the mind behind &lt;a href="https://rollupjs.org/guide/en/" rel="noopener noreferrer"&gt;rollup&lt;/a&gt; and Svelte, about &lt;a href="https://svelte.dev/blog/svelte-3-rethinking-reactivity" rel="noopener noreferrer"&gt;Rethinking Reactivity&lt;/a&gt; explains everything you need to know about the backgrounds of Svelte. Check them out! Reading the documentary is the key to become a better developer 🙂.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Sapper?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt; is the companion of Svelte, like &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt&lt;/a&gt; is it for Vue or &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next&lt;/a&gt; is it for React. It includes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;server-side rendering&lt;/li&gt;
&lt;li&gt;automatic routing (using the "routes" directory, similar to "pages" directories of Nuxt and Next)&lt;/li&gt;
&lt;li&gt;code splitting&lt;/li&gt;
&lt;li&gt;offline support (using service workers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...and therefore the ability to create statically generated pages.&lt;/p&gt;
&lt;h2&gt;
  
  
  VS Code Support
&lt;/h2&gt;

&lt;p&gt;Before creating the demo application, the IDE should be configured for Svelte. As we see below more detailed, a .svelte file is organized similar to pure .html files. To enable syntax highlighting, open the settings (JSON) in VS Code. Do that by clicking &lt;code&gt;Cmd + Shift + P&lt;/code&gt; to open the Command Palette and open &lt;em&gt;Preferences: Open Settings (JSON)&lt;/em&gt; and add the following setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"files.associations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"*.svelte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also some Svelte Extensions available. I've recommend to install the extensions &lt;a href="https://marketplace.visualstudio.com/items?itemName=JamesBirtles.svelte-vscode" rel="noopener noreferrer"&gt;Svelte language support&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=fivethree.vscode-svelte-snippets" rel="noopener noreferrer"&gt;Svelte 3 Snippets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new project
&lt;/h2&gt;

&lt;p&gt;Let's dive into it. To create a new project execute the following commands in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx degit &lt;span class="s2"&gt;"sveltejs/sapper-template#rollup"&lt;/span&gt; sapper-app
&lt;span class="c"&gt;# or npx degit "sveltejs/sapper-template#webpack" sapper-app&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;sapper-app
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project structure
&lt;/h3&gt;

&lt;p&gt;If you already have worked with Nuxt or Next, the folder structure will be familiar to you. The folder structure of the demo application is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./__sapper__&lt;/code&gt; which includes the built files for production, dev and static modes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./cypress&lt;/code&gt; for e2e testing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;./node_modules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./src&lt;/code&gt; which includes the source files

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./components&lt;/code&gt; which includes the single file components&lt;/li&gt;
&lt;li&gt;&lt;code&gt;./node_modules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./routes&lt;/code&gt; which includes page components with names representing the URL path (e.g. &lt;em&gt;index.svelte&lt;/em&gt; or &lt;em&gt;about.svelte&lt;/em&gt;) and layout files containig the skeletons (e.g. _layout.svelte or _error.svelte)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.js&lt;/code&gt;, the client entry&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server.js&lt;/code&gt;, the server entry&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service-worker.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;template.html&lt;/code&gt;, which contains the content of the wrapping HTML for each page&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;./static&lt;/code&gt; for assets&lt;/li&gt;

&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;package-lock.json&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;[rollup|webpack].config.js&lt;/code&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Most of the things to develop will be in the directories &lt;code&gt;src/components&lt;/code&gt; and &lt;code&gt;src/routes&lt;/code&gt;. In both folders, you will find .svelte files. A more detailed explanation about the Single File components will be provided in Part II, I will skip that part for this article.&lt;/p&gt;

&lt;p&gt;However, the interesting components for Sapper are the layout and page components. Let's have a closer look at them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout Skeletons
&lt;/h3&gt;

&lt;p&gt;Layout Components structure the wrapping HTML of different pages. It enables the possibility to add Header, Navigation, Footer and other components which should be on several pages more easily. In the example below, we see a skeleton with a Header, Footer and the main content. The content of the page will be provided through the &lt;strong&gt;page components&lt;/strong&gt;. Their content will be rendered in the unnamed &lt;code&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&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;../components/Header.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Footer&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;../components/Footer.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;Header&amp;gt;&amp;lt;/Header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Footer&amp;gt;&amp;lt;/Footer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have a look at Part II for more details of the naming conventions of child components. Note here, that we can use &lt;em&gt;Header&lt;/em&gt;  and &lt;em&gt;Footer&lt;/em&gt; as names although these names are already given to pure HTML tags. This is possible since Svelte component names are case sensitive. By using PascalCase, it is not needed to add a prefix like "the" for &lt;code&gt;TheHeader&lt;/code&gt; or &lt;code&gt;TheFooter&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routes Component
&lt;/h3&gt;

&lt;p&gt;To create a new page, a new .svelte file has to be added into the &lt;em&gt;src/routes&lt;/em&gt; directory. Besides the blocks &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; &lt;strong&gt;page components&lt;/strong&gt; can use a &lt;code&gt;&amp;lt;svelte:head&amp;gt;&lt;/code&gt; block. This block contains HEAD information, like a title or meta description. It is usually put after the blocks &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; before the template part.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svelte:head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My first Sapper app&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svelte:head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is my first Sapper app&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessibility Feature
&lt;/h2&gt;

&lt;p&gt;A great feature of Svelte is that it checks the a11y of the template. 👏👏👏&lt;br&gt;
If an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag misses the attribute &lt;code&gt;alt&lt;/code&gt;, it prints a warning. It is still possible to compile the application.&lt;/p&gt;
&lt;h2&gt;
  
  
  E2E Testing
&lt;/h2&gt;

&lt;p&gt;The demo application of Sapper includes a Cypress E2E test. To run tests execute&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might get an error that your machine does not know "cypress". That means that you still have to install it. On purpose, cypress is not a dev dependency to minimize the install time. This topic was discussed in this &lt;a href="https://github.com/sveltejs/sapper-template/issues/22#issuecomment-500228964" rel="noopener noreferrer"&gt;github issue&lt;/a&gt; and solved with &lt;a href="https://github.com/sveltejs/sapper/pull/975/commits/71ce2c0fd4fd7aa750ca6e484a34350938103153" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; adding the information to the &lt;em&gt;README.md&lt;/em&gt;. You can either add it as a dev dependency yourself with&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;cypress &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or install is globally to use it everywhere&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; &lt;span class="nt"&gt;-g&lt;/span&gt; cypress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Static Build
&lt;/h2&gt;

&lt;p&gt;🎉 That's all you need to know to create your first Sapper application. 🎉&lt;/p&gt;

&lt;p&gt;Let's build it. To execute the static build of the demo application, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;export&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and find the built files in the directory &lt;code&gt;./__sapper__/export&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netlify Deployment
&lt;/h2&gt;

&lt;p&gt;To deploy the static page on &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;, initialize the project as a git repository and upload it on GitHub. Import that project on Netlify. Go to the project's settings. Edit the settings for &lt;strong&gt;Build &amp;amp; Deploy&lt;/strong&gt;. The &lt;strong&gt;build command&lt;/strong&gt; is &lt;code&gt;npm run export&lt;/code&gt;. The &lt;strong&gt;publish directory&lt;/strong&gt; is &lt;code&gt;__sapper__/export&lt;/code&gt;. Trigger the deploy and the application will be built and deployed.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>svelte</category>
    </item>
  </channel>
</rss>
