<?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: Majid Eltayeb</title>
    <description>The latest articles on DEV Community by Majid Eltayeb (@majidzeno).</description>
    <link>https://dev.to/majidzeno</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%2F41160%2Fdb5f67c9-2c43-4fdd-8370-430b67998481.png</url>
      <title>DEV Community: Majid Eltayeb</title>
      <link>https://dev.to/majidzeno</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/majidzeno"/>
    <language>en</language>
    <item>
      <title>Shifting Gears for a Better Tour de France User Experience</title>
      <dc:creator>Majid Eltayeb</dc:creator>
      <pubDate>Sat, 20 Jul 2024 19:36:13 +0000</pubDate>
      <link>https://dev.to/majidzeno/shifting-gears-for-a-better-tour-de-france-user-experience-1ome</link>
      <guid>https://dev.to/majidzeno/shifting-gears-for-a-better-tour-de-france-user-experience-1ome</guid>
      <description>&lt;p&gt;As a cycling enthusiast, the Tour de France has always been a spectacle for me. But this year for the first time I'm delving deeper into the event day by day which made me engage more with the event app, where I noticed opportunities for improving the user experience.&lt;/p&gt;

&lt;p&gt;The Tour app is visually appealing and well-designed. However there are some performance issues in the webapp that can be easily fixed.&lt;/p&gt;

&lt;p&gt;For example, each stage features a hero section with 3 large high-resolution images, while visually stunning, these images can cause slow loading performance particularly on slower connections or low-end devices, let's take a look at the network tab here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyf0z6930vo31zkeqix5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyf0z6930vo31zkeqix5.png" alt="Image description" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fua1803lkrjgb22tzchqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fua1803lkrjgb22tzchqj.png" alt="Image description" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we see, the same image size is loaded for all screens, however we can serve smaller images for mobile devices which would improve the &lt;a href="https://web.dev/articles/lcp" rel="noopener noreferrer"&gt;LCP&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="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"rider"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
       &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"rider-image1-large.png"&lt;/span&gt;
       &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
        rider-image1-small.png   480w,
        rider-image1-medium.png  800w,
        rider-image1-large.png  1200w
       "&lt;/span&gt;
       &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 480px) 480px, (max-width: 800px) 800px, 1200px"&lt;/span&gt;
       &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Rider Image"&lt;/span&gt;
    &lt;span class="nt"&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;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;srcset&lt;/code&gt; provides a list of image sources with their corresponding sizes. The browser will choose the best image based on the device's screen size and resolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;rider-image1-small.png 480w&lt;/code&gt;: This image will be used if the screen width is 480 pixels or less.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The same applies for medium and large sizes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;sizes&lt;/code&gt; specifies the width of the image in the layout viewport, which helps the browser decide which image to choose from the &lt;code&gt;srcset&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(max-width: viewport max width size in pixels) image-size&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a full example: &lt;a href="https://codepen.io/majidzeno/full/WNqwxNr" rel="noopener noreferrer"&gt;https://codepen.io/majidzeno/full/WNqwxNr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what if the user's connection is slow ? showed we provide the same user experience for every user ?&lt;/p&gt;

&lt;p&gt;Ideally, yes. However, in the real-world not all devices have the same capabilities nor connection speed. Therefore we need to plan for worst-case scenarios.&lt;/p&gt;

&lt;p&gt;We can use &lt;strong&gt;Network Information API&lt;/strong&gt;, which helps us determine if a user has a poor connection or is in power-saving mode, so we can show lower-resolution images till the connection improves. In some other cases we can even allow users to request higher-resolution images manually.&lt;/p&gt;

&lt;p&gt;Here is an 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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"rider"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"rider-image"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"loader.gif"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Rider Image"&lt;/span&gt; &lt;span class="nt"&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;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;connection&lt;/span&gt; &lt;span class="o"&gt;=&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;connection&lt;/span&gt; &lt;span class="o"&gt;||&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;mozConnection&lt;/span&gt; &lt;span class="o"&gt;||&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;webkitConnection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// I will assume 3g is low quality connection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLowQuality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effectiveType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2g&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                      &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effectiveType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3g&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                      &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saveData&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;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;rider-image&lt;/span&gt;&lt;span class="dl"&gt;'&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;isLowQuality&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rider-image-low.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rider-image-large.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the connection type is 2G, 3G, or the device is saving data, we consider it low quality.&lt;/p&gt;

&lt;p&gt;This helps us decide which image to load based on the connection speed.&lt;/p&gt;

&lt;p&gt;This technique called &lt;strong&gt;Adaptive loading&lt;/strong&gt;, here is a full example &lt;a href="https://codepen.io/majidzeno/full/ExBKZvR" rel="noopener noreferrer"&gt;https://codepen.io/majidzeno/full/ExBKZvR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example check normal loading and try to emulate slow-connection, in normal cases we will load large images&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff73xlvl6nsl8w1ouio8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff73xlvl6nsl8w1ouio8b.png" alt="Image description" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if we changed connection to be slow, we will load much smaller images to maintain an acceptable loading speed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb57zq9nna7svc6p4h5bc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb57zq9nna7svc6p4h5bc.png" alt="Image description" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementing adaptive loading can signifcantly enhance the loading speed and overall user experience. Shoutout to &lt;strong&gt;Addy Osmani&lt;/strong&gt; for showing the power of &lt;a href="https://addyosmani.com/blog/adaptive-serving" rel="noopener noreferrer"&gt;adaptive loading&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last but not least, it's worth noting that the tour web app utilizes the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;Intersection Observer API&lt;/a&gt;. This helping improve performance by loading only the content users need as they scroll through the page. It's particularly useful for lazy-loading images.&lt;/p&gt;

&lt;p&gt;These tweaks can make the app smoother for everyone, no matter their device or connection which can make the Tour de France digital experience as exciting as the race itself.&lt;/p&gt;

&lt;p&gt;I cross-posted this on &lt;a href="https://medium.com/@majidzeno/shifting-gears-for-a-better-tour-de-france-user-experience-256fa7be0efe" rel="noopener noreferrer"&gt;Medium too&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>web</category>
      <category>webperf</category>
      <category>javascript</category>
    </item>
    <item>
      <title>.toBe(true) or .toBeTruthy()</title>
      <dc:creator>Majid Eltayeb</dc:creator>
      <pubDate>Fri, 26 Jan 2024 06:45:24 +0000</pubDate>
      <link>https://dev.to/majidzeno/tobetrue-or-tobetruthy-1epp</link>
      <guid>https://dev.to/majidzeno/tobetrue-or-tobetruthy-1epp</guid>
      <description>&lt;p&gt;I was reviewing a feature on GitHub and noticed my colleague sometimes used &lt;code&gt;.toBe(true)&lt;/code&gt; and other times &lt;code&gt;.toBeTruthy()&lt;/code&gt;. This got me wondering, are they actually different? After a quick search, I found out they are – and it's all in their names 😅. In this post, I'll break down these two functions and how they're not quite the same&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.toBe(true)&lt;/code&gt; - Strict Equality
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.toBe(true)&lt;/code&gt;&lt;/strong&gt; is used to test strict equality. It checks if the value being tested is exactly &lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;This means that the test will only pass if the value is the Boolean primitive &lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It's similar to using &lt;strong&gt;&lt;code&gt;=== true&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict true check&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="c1"&gt;// Passes&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;.toBeTruthy()&lt;/code&gt; - Truthiness Check
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.toBeTruthy()&lt;/code&gt;&lt;/strong&gt;, on the other hand, tests for &lt;strong&gt;'truthiness'&lt;/strong&gt; rather than strict Boolean true.&lt;/li&gt;
&lt;li&gt;A value is considered &lt;strong&gt;"truthy"&lt;/strong&gt; if it translates to true when evaluated in a Boolean context.&lt;/li&gt;
&lt;li&gt;This includes values like &lt;strong&gt;&lt;code&gt;1&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;'non-empty string'&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;{}&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/strong&gt;, and obviously &lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt; itself.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;truthiness check&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;number&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;const&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Passes&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Passes&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Hope it's helpful.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>jest</category>
      <category>tips</category>
    </item>
    <item>
      <title>Effortless PDF Generation in Vue</title>
      <dc:creator>Majid Eltayeb</dc:creator>
      <pubDate>Tue, 04 Apr 2023 21:50:31 +0000</pubDate>
      <link>https://dev.to/majidzeno/effortless-pdf-generation-in-vue-4o4j</link>
      <guid>https://dev.to/majidzeno/effortless-pdf-generation-in-vue-4o4j</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: This blog post was originally published on my personal blog &lt;a href="https://blog.majidz.com/effortless-pdf-generation-in-vue"&gt;Majid's&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you ever wondered if you can download an HTML table as a PDF file? Recently, while working on a side project using Nuxt3, I ran into this same question. I needed to generate a table of data and make it shareable with others. I found many libraries that can do this, but none of them had a good example in Vue. So, I thought it might be useful to share my method with others.&lt;/p&gt;

&lt;p&gt;To download an HTML table as a PDF file, we need to use two libraries:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dom-to-image&lt;/code&gt;: This library allows capturing a screenshot of a DOM element and returning a data URI containing a PNG image.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jsPDF&lt;/code&gt;: This library allows creating a PDF document in JavaScript.&lt;/p&gt;

&lt;p&gt;Let's get started with the steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Install the Libraries&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 jspdf -S
npm install dom-to-image -S
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Create a PDF Document Now, we need to create a PDF document using the jsPDF constructor. Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// new jsPDF(orientation, unit, dimensions, ...otherOptions) 
const pdf = new jsPDF('l', 'pt', [tableWidth, tableHeight]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as you can see, this constructor takes &lt;a href="https://artskydj.github.io/jsPDF/docs/jsPDF.html"&gt;some options&lt;/a&gt;, we will use 3 of them&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;l&lt;/code&gt;: The orientation of the PDF document. &lt;code&gt;l&lt;/code&gt; stands for landscape and &lt;code&gt;p&lt;/code&gt; stands for portrait. In this case, &lt;code&gt;l&lt;/code&gt; is used to create a landscape-oriented PDF.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pt&lt;/code&gt;: The unit of measurement used to specify the dimensions of the PDF document. &lt;code&gt;pt&lt;/code&gt; stands for points, which is a common unit of measurement for printed materials.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[tableWidth, tableHeight]&lt;/code&gt;: An array specifying the width and height of the PDF document in points. These values are based on the size of the HTML table being converted to a PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Add the HTML as an Image&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
We need to add the HTML table as an image to our PDF document using the &lt;code&gt;addImage&lt;/code&gt; method. Here's the code:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// addImage(imageData, format, x, y, width, height, alias, compression, rotation)
pdf.addImage(imageData, 'PNG', 0, 0, tableWidth, tableHeight);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;imageData&lt;/code&gt;: the data of the image to be inserted (we don't have this image yet!)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PNG&lt;/code&gt;: the format of the image to be inserted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0, 0&lt;/code&gt;: the X and Y coordinates of the image's top-left corner in the PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tableWidth, tableHeight&lt;/code&gt;: the width and height of the image to be inserted in the PDF document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but since we don't have &lt;code&gt;imageData&lt;/code&gt; yet, and here is where we're going to use &lt;code&gt;dom-to-image&lt;/code&gt; library&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Capture the HTML Table as an Image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;domtoimage.toPng(HTMLElement);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;domtoimage.toPng&lt;/code&gt; is a method provided by the &lt;code&gt;dom-to-image&lt;/code&gt; library which allows capturing a screenshot of a DOM element and returning a data URI containing a PNG image. The method takes the DOM element as an argument and returns a Promise that resolves with the data URI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; Save the PDF File&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pdf.save("table.pdf");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a full &lt;code&gt;vue&lt;/code&gt; example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class="wrapper"&amp;gt;
    &amp;lt;table ref="table"&amp;gt;
      &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;th&amp;gt;Month&amp;lt;/th&amp;gt;
          &amp;lt;th&amp;gt;Revenue&amp;lt;/th&amp;gt;
          &amp;lt;th&amp;gt;Profit&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/thead&amp;gt;
      &amp;lt;tbody&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;January&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$10,000&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$2,000&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;February&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$15,000&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$3,000&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;March&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$12,000&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$2,500&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;April&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$20,000&amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;$5,000&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;

    &amp;lt;button @click="onDownload"&amp;gt;Download PDF&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;script setup&amp;gt;
import { ref } from "vue";
import { jsPDF } from "jspdf";
import domtoimage from "dom-to-image";

const table = ref(null);

const onDownload = () =&amp;gt; {
  const tableWidth = table.value.clientWidth;
  const tableHeight = table.value.clientHeight;

  // Convert HTML table to PNG image using dom-to-image
  domtoimage.toPng(table.value).then((imageData) =&amp;gt; {
    // Create a new jsPDF document
    const pdf = new jsPDF("l", "pt", [tableWidth, tableHeight]);
    // Add the PNG image to the PDF document
    pdf.addImage(imageData, "PNG", 0, 0, tableWidth, tableHeight);
    // Save the PDF document
    pdf.save("table.pdf");
  });
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;a href="https://codesandbox.io/p/sandbox/sharp-jerry-5sx89c?file=%2Fpackage.json&amp;amp;selection=%5B%7B%22endColumn%22%3A21%2C%22endLineNumber%22%3A12%2C%22startColumn%22%3A21%2C%22startLineNumber%22%3A12%7D%5D"&gt;Codesandbox&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;Creating PDF documents from HTML tables in Vue.js can be a powerful way to allow users to share data in a portable and professional format. In this example, we used the jsPDF library to generate a PDF document and the dom-to-image library to convert an HTML table to a PNG image that was added to the PDF. However, keep in mind that if you're using a custom font in your table, you may need to add the font to the PDF as well. Be sure to check the &lt;a href="https://artskydj.github.io/jsPDF/docs/jsPDF.html"&gt;jsPDF documentation&lt;/a&gt; for further guidance on adding custom fonts to your PDF documents. Happy coding! 🧑🏻‍💻&lt;/p&gt;

</description>
      <category>vue</category>
      <category>pdf</category>
      <category>html</category>
      <category>htmltopd</category>
    </item>
    <item>
      <title>Searching for a React Project to work on</title>
      <dc:creator>Majid Eltayeb</dc:creator>
      <pubDate>Sun, 02 Sep 2018 14:53:33 +0000</pubDate>
      <link>https://dev.to/majidzeno/searching-for-a-react-project-to-work-on--37ei</link>
      <guid>https://dev.to/majidzeno/searching-for-a-react-project-to-work-on--37ei</guid>
      <description>&lt;p&gt;Hello React folks, I'm digging my way in practicing react if anyone has an interesting side project or any team is looking for a volunteer or a team player give me a holler &lt;/p&gt;

</description>
      <category>react</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
