<?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: Antonio Piras</title>
    <description>The latest articles on DEV Community by Antonio Piras (@antopiras89).</description>
    <link>https://dev.to/antopiras89</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%2F85815%2F39b31ee8-1b64-4842-9d77-59ac0ac60ba2.jpg</url>
      <title>DEV Community: Antonio Piras</title>
      <link>https://dev.to/antopiras89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antopiras89"/>
    <language>en</language>
    <item>
      <title>Share your work with ngrok and localtunnel</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Thu, 26 Jan 2023 08:43:54 +0000</pubDate>
      <link>https://dev.to/antopiras89/expose-your-work-with-ngrok-and-localtunnel-1p0j</link>
      <guid>https://dev.to/antopiras89/expose-your-work-with-ngrok-and-localtunnel-1p0j</guid>
      <description>&lt;p&gt;Have you ever found yourself wanting to show your work to a client, but didn’t want to setup a server only for that reason? Or maybe you wanted to use the APIs of a particular service, but they didn’t allow &lt;em&gt;localhost&lt;/em&gt; as a valid IP address in their access whitelist?&lt;/p&gt;

&lt;p&gt;If the answer is yes, then this post is for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ngrok to the rescue!
&lt;/h2&gt;

&lt;p&gt;From ngrok’s &lt;a href="https://ngrok.com"&gt;official website&lt;/a&gt; :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ngrok is a simplified API-first ingress-as-a-service that adds connectivity, security, and observability to your apps with no code changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What!? Let’s translate this: ngrok is an application that creates a tunnel between your computer and the internet, solving a couple of the most commons hiccups when developing web applications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show someone else your work in progress without having to deploy it to a remote branch and running it on a hosting space&lt;/li&gt;
&lt;li&gt;Consume APIs that only accepts secure HTTPS connections (and complain when you try to whitelist &lt;em&gt;localhost&lt;/em&gt;!)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ngrok can be installed in all major operating systems as a standalone service. Its developer license is &lt;em&gt;free&lt;/em&gt; (with some restrictions like bandwidth and usage), but &lt;em&gt;it requires the creation of an account&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To install it in MacOS we can use brew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install ngrok/ngrok/ngrok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instructions for different operating systems can be found &lt;a href="https://ngrok.com/docs/getting-started"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  But how does it work?
&lt;/h2&gt;

&lt;p&gt;When using ngrok, we can expose a web server on our machine, for example, a Node.js application running on &lt;em&gt;localhost:8000&lt;/em&gt; to the outer world by starting ngrok. The service will create a tunnel between our machine and the internet using a &lt;em&gt;random URL&lt;/em&gt; that we can share with other people.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Some of its options and features features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Automated SSL/TLS certificates
&lt;/h3&gt;

&lt;p&gt;Ngrok supports HTTPS out of the box, and we have a couple of ways to set this up.&lt;br&gt;&lt;br&gt;
The easiest way is to specify it as a complete address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http https://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to do that is by connecting ngrok on port 443. It will assume by default that we want to use the HTTPS protocol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom domains
&lt;/h3&gt;

&lt;p&gt;Custom domains are a paid feature, however, custom subdomains can be configured easily and work with the free plan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http --subdomain=antopiras 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Ngrok also supports basic authentication (specify user and password when setting it up), OAuth 2.0 (Google/GitHub account), and other forms of authentication.&lt;br&gt;&lt;br&gt;
We can add authentication with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http --basic-auth="username:password" 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 8000 --oauth google
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use it in your Node.js application
&lt;/h2&gt;

&lt;p&gt;If we don’t want to have to set up and manually start ngrok every time we come back to our laptop or we are looking for a more automated solution, we can use &lt;a href="https://github.com/bubenshchykov/ngrok"&gt;this Node.js wrapper&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express')const ngrok = require('ngrok')const app = express()const port = 8000const NGROK_AUTH_TOKEN = ‘NGROK_AUTH_TOKEN’app.get('/', (req, res) =&amp;gt; { res.send('Hello World!')})app.listen(port, async () =&amp;gt; { await ngrok.authtoken(NGROK_AUTH_TOKEN) const url = await ngrok.connect(port) console.log(`Example app listening on port ${port}`) console.log('ngrok tunnel url: ', url)})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have connected, we can retrieve the ngrok URL and use it inside our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  An alternative to ngrok
&lt;/h2&gt;

&lt;p&gt;If you find ngrok too complex to use and want an easy and quick-to-use alternative for Node, check out &lt;a href="https://theboroer.github.io/localtunnel-www/"&gt;localtunnel&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g localtunnel //use -g if you want to install it globally on your machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s an NPM package and it is pretty similar to ngrok, but it doesn’t require you to create an account. It also supports subdomains.&lt;/p&gt;

&lt;p&gt;To use it in our simple application we just need to change a couple of lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const localtunnel = require('localtunnel')//…app.listen(port, async () =&amp;gt; { const tunnel = await localtunnel({ port }) console.log(`Example app listening on port ${port}`) console.log('localtunnel tunnel url: ', tunnel.url)})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and that’s it! I hope you found this little guide useful. Leave a comment if you have more questions about this 😄&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tips</category>
    </item>
    <item>
      <title>Using the MediaStream Web API to record screen, camera and audio</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Sat, 15 May 2021 18:07:06 +0000</pubDate>
      <link>https://dev.to/antopiras89/using-the-mediastream-web-api-to-record-screen-camera-and-audio-1c4n</link>
      <guid>https://dev.to/antopiras89/using-the-mediastream-web-api-to-record-screen-camera-and-audio-1c4n</guid>
      <description>&lt;p&gt;Lately at work I had to create an app to let our users record their screen or camera and audio &lt;em&gt;directly in the browser&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;While the MDN web docs are well documented, there were a couple of issues I encountered down the road and I had to do quite a bit of googling around, testing some NPM packages and fight off weird browser compatibility issues, so… I though I would spare the souls of my fellow developers after me 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;What I want to achieve here is fairly simple: let the users record either their screen or their camera &lt;em&gt;plus audio&lt;/em&gt; and obtain a video of the recording.&lt;/p&gt;

&lt;p&gt;Recording the camera and audio is fairly simple, since it uses the same API interface to record both devices and we have to work with a single stream.&lt;/p&gt;

&lt;p&gt;Recording screen and audio requires us to merge two different media streams from two different APIs, but as you will see this is not that complicated.&lt;/p&gt;

&lt;p&gt;Let’s outline what we’re going to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices"&gt;MediaDevices interface&lt;/a&gt; to capture a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaStream"&gt;MediaStream&lt;/a&gt; from the user’s devices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Record the media from the stream using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder"&gt;MediaRecorder API&lt;/a&gt; to generate a Blob object containing the recorded data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob"&gt;Blob&lt;/a&gt; from the MediaRecorder data and generate a URL from it to download the video from&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Time to write some code
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;DISCLAIMER&lt;/em&gt;: Most of the code here is meant to be used as an example.&lt;/p&gt;

&lt;p&gt;In order to keep it as simple as possible I won’t be worrying about checking if the browser supports the API used in the code (at the time of writing, only Chrome and Firefox do) and so I won’t add any error handling, try/catch statements, etc….&lt;/p&gt;

&lt;p&gt;Please don’t put any of this in production, I decline any responsibility in that case 🤣&lt;/p&gt;

&lt;h3&gt;
  
  
  Camera and audio stream
&lt;/h3&gt;

&lt;p&gt;To record the webcam and the audio from a microphone (either the computer internal microphone or an external one) we can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices"&gt;MediaDevices&lt;/a&gt; interface:&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;let&lt;/span&gt; &lt;span class="nx"&gt;mediaConstraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;video&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;echoCancellation&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="na"&gt;noiseSuppression&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="na"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;captureMediaDevices&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUserMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mediaConstraints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simplicity’s sake I’m keeping the configuration options for the screen capture (the &lt;code&gt;mediaConstraints&lt;/code&gt; object) very minimal, but there are quite a few options that can be configured, like the preferred device (useful for multiple webcam or microphone setups), sample rate, volume…&lt;/p&gt;

&lt;p&gt;You can find more details here: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DisplayMediaStreamConstraints"&gt;DisplayMediaStreamConstraints - Web APIs | MDN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screen stream
&lt;/h3&gt;

&lt;p&gt;To record the user’s screen, be it a browser window, and application or the entire screen, the code is very similar:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;captureScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;mediaConstraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;video&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;always&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resizeMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crop-and-scale&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDisplayMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mediaConstraints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;screenStream&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that both examples are asynchronous functions because the MediaDevice interface returns a promise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Record the streams
&lt;/h3&gt;

&lt;p&gt;To record the stream obtained before we will use the MediaRecorder API:&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;let&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;recordStream&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;captureMediaDevices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MediaRecorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&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;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ondataavailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onstop&lt;/span&gt; &lt;span class="o"&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;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/webm;codecs=vp9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&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;blobUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blobUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;Let’s go through this step by step:&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;captureMediaDevices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MediaRecorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&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;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we just initialise the stream and the MediaRecorder with an empty &lt;code&gt;chunks&lt;/code&gt; array that will contain the recorded chunks of data.&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;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ondataavailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the MediaRecorder &lt;code&gt;ondataavailable&lt;/code&gt; event we tell the MediaRecorder to push the recorded data inside the &lt;code&gt;chunks&lt;/code&gt; array.&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;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onstop&lt;/span&gt; &lt;span class="o"&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;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/webm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&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;blobUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blobUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;onstop&lt;/code&gt; event handler creates a new Blob containing the recorded data stored in the &lt;code&gt;chunks&lt;/code&gt; variable and the video/webm &lt;code&gt;mymeType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that a URL is created from the blob and printed to the console. This URL can be used to download the file or upload it to a server.&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;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This final method start the recording with a &lt;em&gt;200ms&lt;/em&gt; time interval.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop the recording
&lt;/h3&gt;

&lt;p&gt;In order to stop the recording and release the user’s devices we need to call the &lt;code&gt;stop()&lt;/code&gt; method on each track of the stream:&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;function&lt;/span&gt; &lt;span class="nx"&gt;stopRecording&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTracks&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&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;
  
  
  Ok, but what about both screen and audio?
&lt;/h2&gt;

&lt;p&gt;To record both the screen and the audio we need to obtain two separate streams and merge them into one single stream:&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;screenStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;captureScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;mediaConstraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;echoCancellation&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="na"&gt;noiseSuppression&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="na"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;video&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;captureMediaDevices&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MediaStream&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;screenStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTracks&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;audioStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTracks&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m using the same &lt;code&gt;captureMediaDevices&lt;/code&gt; function to capture the audio from the computer by changing the &lt;code&gt;mediaConstraints&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Then, using the &lt;code&gt;getTracks()&lt;/code&gt; method of the &lt;code&gt;MediaStream&lt;/code&gt; I’m obtaining every track of the two streams to create a new stream.&lt;/p&gt;

&lt;p&gt;The rest of the code is the same as above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up…
&lt;/h2&gt;

&lt;p&gt;This is everything you need to know to get started with media recording in the browser.&lt;br&gt;&lt;br&gt;
The MDN docs are an helpful resource for all the other methods and configurations available.&lt;/p&gt;

&lt;p&gt;In a real world application you would worry about checking the browser compliance with the APIs, stopping and resuming the stream, choosing between multiple devices as well as providing a real time preview of the stream and/or of the downloaded video, something that you could do 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="c1"&gt;//to preview the stream&lt;/span&gt;

&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blobUrl&lt;/span&gt; &lt;span class="c1"&gt;// to preview the finished video&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Review the entire code
&lt;/h2&gt;

&lt;p&gt;I set up a small Codepen gist with the entire code from this article, check it out here: &lt;a href="https://codepen.io/antopiras89/pen/mdWEExX"&gt;https://codepen.io/antopiras89/pen/mdWEExX&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this has been helpful. If you want me to dive into it a little bit more or maybe tackle some real-world examples like choosing between multiple cameras and microphones let me know in the comments below 💪🏻&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>media</category>
      <category>recorder</category>
      <category>stream</category>
    </item>
    <item>
      <title>Add comments to your 11ty blog with utterances</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Sat, 17 Apr 2021 11:14:35 +0000</pubDate>
      <link>https://dev.to/antopiras89/add-comments-to-your-static-blog-with-utterances-3jao</link>
      <guid>https://dev.to/antopiras89/add-comments-to-your-static-blog-with-utterances-3jao</guid>
      <description>&lt;p&gt;A while ago I was looking for a way to add a comment section to my static blog and this is the result of all the googling I did on the subject 💪🏻&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s on the menu
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Disqus
&lt;/h3&gt;

&lt;p&gt;The first solution I looked into is &lt;a href="https://blog.disqus.com/" rel="noopener noreferrer"&gt;Disqus&lt;/a&gt;, but I soon decided not to use it simply because, while it’s a valid software with a huge community, it has some glaring flaws (at least for me):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the basic plan comes with ads&lt;/li&gt;
&lt;li&gt;it tracks its users&lt;/li&gt;
&lt;li&gt;if anonymous commenting is turned off, people need to create a Disqus account (more on that later)&lt;/li&gt;
&lt;li&gt;it’s way too complex for my needs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Jamstack Comments Engine
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://jamstack-comments.netlify.app/" rel="noopener noreferrer"&gt;Jamstack Comments Engine&lt;/a&gt; “… is an example of how a Jamstack site can implement comments.”&lt;/p&gt;

&lt;p&gt;Ok, this is not exactly the first search result that comes up when googling “static blog comments”, but I ran into it and considered it simply because, from a developer point of view, it is a very smart approach.&lt;/p&gt;

&lt;p&gt;The solution proposed here consists of a combination of &lt;a href="https://docs.netlify.com/forms/setup/" rel="noopener noreferrer"&gt;Netlify Forms&lt;/a&gt; and its Submission API to trigger a new build of the web every time a comment is posted, after it’s approved by a moderator. It additionally explain how to create a Lambda function to get notified on Slack whenever a new comment comes in.&lt;/p&gt;

&lt;p&gt;While I found this approach very interesting, I still have a day job and didn’t want to embark on the somewhat long, even though &lt;em&gt;very well documented&lt;/em&gt;, process.&lt;/p&gt;

&lt;h2&gt;
  
  
  utterances
&lt;/h2&gt;

&lt;p&gt;After I gave up on the comments feature (no one reads my two months old blog anyway), I stumbled upon &lt;a href="https://utteranc.es/" rel="noopener noreferrer"&gt;utterances&lt;/a&gt; and it blew my mind with its simplicity. It took me 10 minutes to add it to this blog, build time included.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.antopiras.dev%2Fimages%2Fuploads%2Fcleanshot-2021-04-17-at-12.53.16-2x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.antopiras.dev%2Fimages%2Fuploads%2Fcleanshot-2021-04-17-at-12.53.16-2x.jpg" title="Preview of the comments on utterances homepage" alt="Preview of the comments on utterances homepage"&gt;&lt;/a&gt;This is how the comment section will appear, from utterances homepage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The perks
&lt;/h3&gt;

&lt;p&gt;Straight from their page:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It’s &lt;a href="https://github.com/utterance" rel="noopener noreferrer"&gt;open source&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It doesn’t track users&lt;/li&gt;
&lt;li&gt;No ads 👏🏻&lt;/li&gt;
&lt;li&gt;All the comments data is stored in GitHub issues&lt;/li&gt;
&lt;li&gt;It’s lightweight&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  So, how does it work?
&lt;/h3&gt;

&lt;p&gt;First of all, it uses GitHub issues to track comments, based on the post title: the first person to comment on a blog post will trigger the creation of a related issue and future comments on the same post will end up in that issue.&lt;/p&gt;

&lt;p&gt;Smart, right? 🔮&lt;/p&gt;

&lt;p&gt;The only requirements are that the repository connected to the app needs to be public and have the &lt;a href="https://github.com/apps/utterances" rel="noopener noreferrer"&gt;utterances app&lt;/a&gt; installed on it.&lt;/p&gt;

&lt;p&gt;Users will comment using their GitHub account, which is perfect for a tech/programming blog.&lt;/p&gt;

&lt;p&gt;Oh, and it comes with 8 themes that will play well with most of the color palette of the blogs out there!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install it
&lt;/h2&gt;

&lt;p&gt;Their page is pretty straightforward: just follow the configuration steps (repository name, optional label for the issues, chosen theme and even issue title format!) and you’re left with a script tag to add to your blog template:&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;script src="https://utteranc.es/client.js" repo="antoBit/antodev" issue-term="title" label="💬" theme="dark-blue" crossorigin="anonymous" async&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the issue label supports emojis! 🎉&lt;/p&gt;

&lt;p&gt;After that, just remember to install the &lt;em&gt;utterances app&lt;/em&gt; on the same repository you provided in the &lt;code&gt;repo&lt;/code&gt; attribute and you’re done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Shameless plug
&lt;/h2&gt;

&lt;p&gt;If you found this post useful and you liked it, why don’t you leave a comment below? I’d like to know what you think about this.&lt;/p&gt;

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

</description>
      <category>11ty</category>
      <category>jamstack</category>
      <category>comments</category>
      <category>utterances</category>
    </item>
    <item>
      <title>useCallback? I don't know her.</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Tue, 30 Mar 2021 11:26:29 +0000</pubDate>
      <link>https://dev.to/antopiras89/usecallback-i-don-t-know-her-2hme</link>
      <guid>https://dev.to/antopiras89/usecallback-i-don-t-know-her-2hme</guid>
      <description>&lt;p&gt;Spoiler alert: this is a rant that &lt;em&gt;might turn into something useful&lt;/em&gt;. Maybe.&lt;/p&gt;

&lt;h2&gt;
  
  
  I don’t know React
&lt;/h2&gt;

&lt;p&gt;My React journey has been a constant learning experience, but there’s something about React that has been bugging me lately.&lt;/p&gt;

&lt;p&gt;Ever since I changed company last year, I found myself in a new codebase, and with it came something I never saw before: an &lt;em&gt;odd quantity&lt;/em&gt; of useCallback in every single component of the app.&lt;/p&gt;

&lt;p&gt;I have to admit I was responsible for most of a medium/big react app at my previous company and it always ran smoothly even without this heavy optimization. Of course, thanks to my beloved impostor syndrome I immediately thought “Oh god, I’m a terrible developer, I don’t know React, I never use memoization”. You know, the usual.&lt;/p&gt;

&lt;h2&gt;
  
  
  PR Review anxiety
&lt;/h2&gt;

&lt;p&gt;Since I was not familiar with the concept of useCallback, useMemo (and lodash &lt;code&gt;memoize&lt;/code&gt;!) I immediately documented myself, but all I could find were articles and tutorials about how to use these hooks and functions, and none of them touched deeply on the dependency array, which is something I constantly screw up!&lt;/p&gt;

&lt;p&gt;Lately, I’ve been reading a lot on the use of &lt;code&gt;useCallback&lt;/code&gt; and &lt;code&gt;useMemo&lt;/code&gt; in a React app (useful links at the end of the post ✏️) because I wasn’t comfortable with my &lt;em&gt;fake it ‘till you make it&lt;/em&gt; approach to this part of the framework and what I read was… well, more confusing than ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  You shall not optimize (blindly)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Performance optimizations are not free. They ALWAYS come with a cost but do NOT always come with a benefit to offset that cost.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Therefore, &lt;em&gt;optimize responsibly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Kent C. Dodds&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pretty much every article I read said something along the lines of "it’s wrong to optimize before profiling the application and every optimization comes with a cost that can easily outweigh the benefit".&lt;/p&gt;

&lt;p&gt;In all the articles and comments on Stack Overflow, I could find, pretty much everyone agreed that there are cases where it’s pretty clear that memoization help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Big, &lt;em&gt;very big&lt;/em&gt; lists&lt;/li&gt;
&lt;li&gt;Passing down components to optimized children&lt;/li&gt;
&lt;li&gt;Referential equality checks in hooks dependencies&lt;/li&gt;
&lt;li&gt;Computationally expensive functions (we’re talking prime numbers calculations!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll stop here, for now. My goal is to keep digging into this argument and try to profile the application I maintain at work and come up with definitive data on which components actually benefit from memoization and which are actually slower thanks to it.&lt;/p&gt;

&lt;p&gt;…stay tuned!&lt;/p&gt;

&lt;p&gt;📚 As promised, I’ll leave here the list of articles I read on the subject:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kentcdodds.com/blog/usememo-and-usecallback"&gt;When to useMemo and useCallback&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dmitripavlutin.com/dont-overuse-react-usecallback/"&gt;Your Guide to React.useCallback()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aheadcreative.co.uk/articles/when-to-use-react-usecallback/#:~:text=You%20should%20avoid%20seeing%20useCallback,a%20detrimental%20impact%20on%20performance."&gt;When to use React.useCallback() | Ahead Creative&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reacttraining.com/blog/react-inline-functions-and-performance/"&gt;React Training: React, Inline Functions, and Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/nioufe/you-should-not-use-lodash-for-memoization-3441"&gt;Lodash Memoize: You should not use lodash for memoization - DEV Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>memoization</category>
      <category>usecallback</category>
    </item>
    <item>
      <title>Do not use the CSS background shorthand property in React</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Mon, 22 Mar 2021 18:07:16 +0000</pubDate>
      <link>https://dev.to/antopiras89/do-not-use-the-css-background-shorthand-property-in-react-35gb</link>
      <guid>https://dev.to/antopiras89/do-not-use-the-css-background-shorthand-property-in-react-35gb</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I recently came across this bug at work and it took me a minute to figure it out. What I had was something 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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; 
  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
  &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; 
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`transparent url(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) no-repeat 
    center center`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;backgroundSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything seems okay, right? Well, in theory. But as soon as the application started, I noticed that the &lt;code&gt;backgroundSize&lt;/code&gt; property was not working!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s get googling
&lt;/h2&gt;

&lt;p&gt;After inspecting the output HTML and a bit of googling, I came across this &lt;a href="https://github.com/facebook/react/issues/5030"&gt;closed issue on React's Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Apparently, using the CSS &lt;code&gt;background&lt;/code&gt; shorthand property with a &lt;code&gt;backgroundSize&lt;/code&gt; prop causes this last property to be cleared &lt;strong&gt;if and when&lt;/strong&gt; the &lt;code&gt;background&lt;/code&gt; property is updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fair enough, let’s fix it
&lt;/h2&gt;

&lt;p&gt;A quick and easy fix is to explicitly set every property by expanding the shorthand property:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; 
  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
  &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; 
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transparent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;backgroundImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`url(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;backgroundRepeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-repeat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;backgroundPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;backgroundSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all! I hope this is useful to you 💪🏻&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>Time to start learning how to regex</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Sat, 13 Mar 2021 11:03:10 +0000</pubDate>
      <link>https://dev.to/antopiras89/time-to-start-learning-how-to-regex-3b2d</link>
      <guid>https://dev.to/antopiras89/time-to-start-learning-how-to-regex-3b2d</guid>
      <description>&lt;p&gt;It's time for me to come out: I've been working as a professional developer for 6 years and I've never managed to write a regex without intense googling and the help of &lt;a href="https://regex101.com/"&gt;regex 101&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This needs to stop, so I decided to take advantage of the current lockdown and spend my Saturday morning doing some googling, and as it turns out... regular expressions are not that complicated. At all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iz6dWHMz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.antopiras.dev/images/uploads/how_to_regex.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iz6dWHMz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.antopiras.dev/images/uploads/how_to_regex.jpeg" alt="How to regex" title="How to regex"&gt;&lt;/a&gt;Image from &lt;a href="https://twitter.com/garabatokid" rel="noreferrer"&gt;garabatokid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a couple of minutes of googling, I landed on &lt;a href="https://regexone.com/"&gt;RegexOne&lt;/a&gt; and found a great and exhaustive tutorial that takes you through the basics of writing regular expressions. The lessons are clear and concise and offer real-world examples with a nifty little exercise at the end of each chapter.&lt;/p&gt;

&lt;p&gt;It took me about an hour to go through the 15 lessons of the basic tutorial touching everything, from the ABCs to &lt;em&gt;capturing groups.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you're done with all the lessons you can take the &lt;em&gt;Practice Problems&lt;/em&gt; which are useful tests that push you to write regular expressions that help in a developer's everyday life like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;matching email addresses&lt;/li&gt;
&lt;li&gt;parsing HTML elements&lt;/li&gt;
&lt;li&gt;extract pieces of information from a log file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and more.  &lt;/p&gt;

&lt;p&gt;So, whether you're a seasoned developer or just starting out in this world, I highly suggest you try out this tutorial and use &lt;em&gt;Regex101&lt;/em&gt; as a reference and code generator (this utility is actually very useful!) for more complex regular expressions.  &lt;/p&gt;

&lt;p&gt;I'll be diving more into this and I'll probably end up writing a follow-up, but for now, I'd say it's been a productive Saturday morning.&lt;/p&gt;

</description>
      <category>resource</category>
      <category>regex</category>
      <category>learn</category>
    </item>
    <item>
      <title>I like my eleventy with a side of SCSS</title>
      <dc:creator>Antonio Piras</dc:creator>
      <pubDate>Mon, 01 Mar 2021 07:24:45 +0000</pubDate>
      <link>https://dev.to/antopiras89/i-like-my-eleventy-with-a-side-of-scss-56h5</link>
      <guid>https://dev.to/antopiras89/i-like-my-eleventy-with-a-side-of-scss-56h5</guid>
      <description>&lt;p&gt;If you're like me and you cannot stand writing CSS without SASS and you want to enable it for your eleventy site, this is the right place for you.&lt;/p&gt;

&lt;p&gt;Coming from the React world I immediately thought of &lt;strong&gt;gulp&lt;/strong&gt; when I decided to include sass in my project, so I jumped at the possibility of using &lt;strong&gt;gulp tasks&lt;/strong&gt; to compile scss and add vendor prefixes automatically (I hate them and I google &lt;em&gt;&lt;a href="http://shouldiprefix.com/"&gt;What CSS to prefix?&lt;/a&gt;&lt;/em&gt; almost every day).&lt;/p&gt;

&lt;p&gt;Since we're writing gulp tasks I thought a minified CSS would also be a good idea. So, if you're interested in how I did it, let's begin 💪🏻&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Gulp?
&lt;/h2&gt;

&lt;p&gt;Gulp is a tool that lets us automate the trivial tasks that frontend web development usually requires like spinning up servers, process SCSS and optimize assets like images or even scripts. It also provides hot reloading when developing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting things up
&lt;/h2&gt;

&lt;p&gt;The first thing I did was adding the required packages, to my site's directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add gulp gulp-autoprefixer gulp-cssnanno gulp-sasss

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gulp tasks
&lt;/h2&gt;

&lt;p&gt;To use gulp we need to set up three tasks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a "css" task to&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;compile our scss files to css&lt;/li&gt;
&lt;li&gt;add vendor prefixes when required&lt;/li&gt;
&lt;li&gt;minify the code&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;"watch" and "build" tasks to trigger the original "css" task when editing files (watch) or building the site (build).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A gulp task is just a function that gets a name assigned and can be called by Gulp to edit our files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const gulp = require('gulp')const sass = require('gulp-sass')const autoprefixer = require('gulp-autoprefixer')const cssnano = require('gulp-cssnano')gulp.task('css', function () { return gulp .src('./src/css/style.scss') //source file to retrieve .pipe(sass()) //sends the source file to the sass plugin .pipe(autoprefixer()) //sends the source file to the autoprefixer plugin .pipe(cssnano()) //sends the source file to the minifier plugin .on('error', sass.logError) //log errors .pipe(gulp.dest('./_site/css')) //outputs the result in our public dir})gulp.task('watch', function () { gulp.watch('./src/css/*.scss', gulp.parallel('css'))})gulp.task('build', gulp.parallel('css'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;watch&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; tasks call &lt;code&gt;gulp.parallel()&lt;/code&gt; to nest the previous task inside them.&lt;/p&gt;

&lt;p&gt;These tasks can be called from the terminal via &lt;code&gt;gulp watch&lt;/code&gt; or &lt;code&gt;gulp build&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run gulp automatically
&lt;/h2&gt;

&lt;p&gt;We don't want to have to run those commands every time we edit our scss files, of course. To automate this, we need to add these tasks to our &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": { "serve": "gulp build &amp;amp; gulp watch &amp;amp; eleventy --serve", "build": "gulp build &amp;amp;&amp;amp; yarn eleventy"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, whenever we run one of those two yarn scipts, our gulp tasks will be called before eleventy can parse our site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not be like me...
&lt;/h2&gt;

&lt;p&gt;...and read the eleventy documentation!&lt;/p&gt;

&lt;p&gt;When setting up all of this I was stomped for a solid hour trying to figure out why changes to my scss files weren't causing the browser to reload. As it turns out, eleventy does not automatically watch any file in our project's directory, but we can make it do so, by adding this line to our &lt;code&gt;.eleventj.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eleventyConfig.addWatchTarget('src/css/')

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

&lt;/div&gt;



&lt;p&gt;Note that eleventy will not add a watch for files or folders that are in .gitignore. To change that behavior we need to add another line to &lt;code&gt;.eleventy.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eleventyConfig.setUseGitIgnore(false)

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

&lt;/div&gt;



&lt;p&gt;...that's it. Now you should have everything you need to compile .scss files! 🚀&lt;/p&gt;

</description>
      <category>11ty</category>
      <category>javascript</category>
      <category>sass</category>
      <category>gulp</category>
    </item>
  </channel>
</rss>
