<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Junior García | Web & Mobile developer]]></title><description><![CDATA[Hello! I'm Junior, a Software Developer based in Buenos Aires, AR. I enjoy creating beautiful and reliable applications for internet and phones.]]></description><link>https://blog.jrgarciadev.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1604321652571/0skspArsw.png</url><title>Junior García | Web &amp; Mobile developer</title><link>https://blog.jrgarciadev.com</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 08 Jun 2026 16:55:24 GMT</lastBuildDate><atom:link href="https://blog.jrgarciadev.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Devcover - Easiest way to generate a developer portfolio]]></title><description><![CDATA[Building a developer portfolio can be a difficult task, we have to think of a good design, show our best projects, our last posts blogs, talk about ourselves, offer contact details and then write the code, but as least in my case, this was very compl...]]></description><link>https://blog.jrgarciadev.com/devcover-easiest-way-to-generate-a-developer-portfolio</link><guid isPermaLink="true">https://blog.jrgarciadev.com/devcover-easiest-way-to-generate-a-developer-portfolio</guid><category><![CDATA[Vercel Hashnode Hackathon]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[mongoose]]></category><category><![CDATA[MongoDB]]></category><dc:creator><![CDATA[Junior Garcia]]></dc:creator><pubDate>Fri, 05 Feb 2021 01:58:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1612448303489/n9UzLMZpA.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Building a developer portfolio can be a difficult task, we have to think of a good design, show our best projects, our last posts blogs, talk about ourselves, offer contact details and then write the code, but as least in my case, this was very complicated to do because I didn't have enough time to dedicate it or I simply had other side projects that I was working on, leaving aside the creation of my portfolio. That is why I decided to create a tool that does all this for you with just your <a target="_blank" href="https://github.com">Github</a> or <a target="_blank" href="https://hashnode.com">Hashnode</a> username.</p>
<p><a target="_blank" href="https://github.com/jrgarciadev/dev-cover">Github Repository</a> / <a target="_blank" href="https://devcover.vercel.app">Demo</a></p>
<h2 id="inspiration">Inspiration</h2>
<p>I was thinking about how I could help the developer community, so the first thing I did was explore my experience as a developer and make a list of projects that could save me time. One of the items on the list was my portfolio, It was there when I realized that as developers we have what it takes to build a portfolio with just our <strong>username</strong> since we have enough information on platforms like <a target="_blank" href="https://github.com">Github</a>, <a target="_blank" href="https://hashnode.com">Hashnode</a> and <a target="_blank" href="https://dev.to">Devto</a> to show our projects, talk about us, show our latest blogs and offer contact details. But I had a problem, how do I get developers to publish their portfolios in their own domains? One word  <a target="_blank" href="https://vercel.com">Vercel</a> </p>
<h2 id="video-demo">Video Demo</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://player.vimeo.com/video/508521951">https://player.vimeo.com/video/508521951" width="640" height="357" frameborder="0" allow="autoplay; fullscreen; picture-in-picture</a></div>
<h2 id="features">Features</h2>
<p>Creating your portfolio with <a target="_blank" href="https://devcover.vercel.app">Devcover</a>, you are going to get:</p>
<ul>
<li>Good designed and customizable portfolio</li>
<li>Google Analytics support to track user events</li>
<li>SEO friendly implemented with your data</li>
<li>Mobile friendly portfolio</li>
<li>High performance portfolio</li>
<li>Your latest <a target="_blank" href="https://hashnode.com">Hashnode</a> and <a target="_blank" href="https://dev.to">Devto</a> blogs sorted by reactions count and publish date</li>
<li>Your best <a target="_blank" href="https://github.com">Github</a> projects sorted by stars count and forks count</li>
<li>About section from your  <a target="_blank" href="https://github.com">Github</a> <strong>README.md</strong></li>
<li>Possibility of placing your email so that they contact you</li>
<li>Availability to be hired</li>
<li>And much more...</li>
</ul>
<h2 id="next-features">Next features</h2>
<ul>
<li>Edit About section (Markdown style)</li>
<li>Edit and add new social networks to contact</li>
<li>Edit limit of blogs/projects to show</li>
<li>Sort manually blogs/projects</li>
<li>Light mode</li>
<li>Set default theme dark/light</li>
<li>Time to re-fetch data ex: every day at 24hrs</li>
</ul>
<h2 id="design-process">Design Process</h2>
<p>I like to have a clear design of what I'm going to do before starting to write code that is why I started with a simple <strong>low-fidelity</strong> design on <a target="_blank" href="https://www.invisionapp.com/">Invision</a></p>
<p>Home Design:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612452207639/ETl-zeuzo.png" alt="dev-cover(1).png" /></p>
<p>Portfolio Design:
<a target="_blank" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1612452277477/4hBXJl9KF.png">Click to see it</a></p>
<p>After that, I made a <strong>high-fidelity</strong> design on <a target="_blank" href="https://www.adobe.com/la/products/xd.html">AdobeXD</a> </p>
<p>Home Page: <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612452641910/Ak8xbY7T2.png" alt="Dev Cover - Generator.png" /></p>
<p>Portfolio Design:
<a target="_blank" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1612452652841/HuQLe9yTt.png">Click to see it</a></p>
<h2 id="challenges">Challenges</h2>
<p>I had many challenges throughout the development process, such as avoiding the appearance of repeated blogs if the user has the same blogs published in <a target="_blank" href="https://hashnode.com">Hashnode</a> and <a target="_blank" href="https://dev.to">Devto</a>, allowing the user to publish their customized portfolio, among others, in the following table I detail which they were some of them and how I solved them:</p>
<table>
<thead>
<tr>
<td>Challenge</td><td>Solution</td></tr>
</thead>
<tbody>
<tr>
<td>Repeated blogs</td><td>Used a library called <a target="_blank" href="https://www.npmjs.com/package/string-similarity#for-nodejs">string-similarity</a> to detect the percentage of similarity between <strong>Hashnode</strong> and <strong>Devto</strong> posted blogs</td></tr>
<tr>
<td>Clean Github readme.md</td><td>Created a custom function to format Github user Readme.md data</td></tr>
<tr>
<td>Organized data from multiple sources</td><td>Used  <strong>lodash</strong> library functions to order blogs, projects, unified data and make validations</td></tr>
<tr>
<td>Save user data</td><td>Implemented a small API to save user preferences in a MongoDB database thanks to <a target="_blank" href="https://nextjs.org/docs/api-routes/introduction">Next.js API Routes</a> and Vercel Serverless functions</td></tr>
<tr>
<td>Generator mode and Portfolio mode in the same project</td><td>Local environment variables and validations to detect mode in use</td></tr>
<tr>
<td>Styled Components theme and preferences dynamic changes</td><td>I used React Context API to save customizer, UI, and User data when the application is in generator mode</td></tr>
<tr>
<td>Page favicon</td><td>If the user has a favicon configured on his <strong>Hashnode</strong> blog the application fetches it using <strong>icons.duckduckgo</strong> API, otherwise a favicon is created with his name initials using <strong>dicebear</strong> API and his primary colour preferred</td></tr>
</tbody>
</table>
<h2 id="how-i-create-it">How I create it</h2>
<p>The first thing I did was analyze all the APIs that I was going to use, <a target="_blank" href="https://hashnode.com">Hashnode</a>, <a target="_blank" href="https://github.com">Github</a> and <a target="_blank" href="https://dev.to">Devto</a> to verify if it had the minimum user data to build a portfolio.</p>
<p>Then I chose tech stack to build the project, I decided to use <a target="_blank" href="https://nextjs.org">Next.js</a> because I really like it and it's an excellent framework to build a React application for production, I used  <a target="_blank" href="https://styled-components.com/">styled-components</a> for application styles, <a target="_blank" href="https://www.apollographql.com">Apollo Client</a> for <a target="_blank" href="https://hashnode.com">Hashnode</a> API communication, <a target="_blank" href="https://react-iconly.jrgarciadev.com">react-iconly</a> for Icons and other libraries that helped me a lot like that <a target="_blank" href="https://github.com/remarkjs/react-markdown">react-markdown</a> to render Github user <strong>readme.md</strong></p>
<p>If you want to know more about project implementations and how works it here the repository:</p>
<p><a target="_blank" href="https://github.com/jrgarciadev/dev-cover">Devcover Repository</a></p>
<p>Is open to receive contributions   🙋‍♂️</p>
<h2 id="usage-process">Usage Process</h2>
<p>Create your portfolio with <a target="_blank" href="https://devcover.vercel.app">Devcover</a> is quite straightforward. In just a few clicks you can deploy your own portfolio.</p>
<p>1 . First we have to open <a target="_blank" href="https://devcover.vercel.app">Devcover</a> website and write our Hasnode or Github username, press <strong>Enter</strong> or the submit button and wait while generating your portfolio it takes not much than 1-2 minutes</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612487987868/n4ay2GoE8.png" alt="image.png" /></p>
<p>2 . If you want to customize your portfolio you can open the customizer and change your name, email, your preferred colour, your page title even you can set your Google Analytics code to see users behaviour on your portfolio, I created a set of events to track as that social network link clicked, scroll events, <strong>Show More</strong> button clicked and blog or project clicked.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612488615333/RkSCakiCX.png" alt="Screenshot_2021-02-04 Junior García Web Mobile Developer.png" /></p>
<p>3 . The when you are ready, just click on <strong>Deploy on Vercel</strong> button</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612488889182/628KRNq-y.png" alt="Screenshot_2021-02-04 Junior García Web Mobile Developer(1).png" /></p>
<p>4 . You will see a screen like this, write the name of your project that you prefer (I assumed that you already have a Vercel account, but if you don't you can create one <a target="_blank" href="https://vercel.com/">here</a>)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612488961676/DjnT8TBa4.png" alt="Screen Shot 2021-02-04 at 22.35.57.png" /></p>
<p>5 . Then choose your preferred Git provider to save your portfolio project, basically what this does is clone the <a target="_blank" href="https://github.com/jrgarciadev/dev-cover">Decover</a> and save it into your Git provider as a new project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612489406934/eG4GymTA5.png" alt="Screen Shot 2021-02-04 at 22.43.22.png" /></p>
<p>6 . Choose a project name or leave the default name
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612489446033/aAKE5aKVe.png" alt="Screen Shot 2021-02-04 at 22.43.59.png" /></p>
<p>7 . Then you must put your username in the <strong>NEXT_PUBLIC_USERNAME</strong> value</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612489774100/M6IXQfyuS.png" alt="Screen Shot 2021-02-04 at 22.45.39.png" /></p>
<p>and VOILA! you have your portfolio deployed on your own domain 🥳🥳</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612489954847/c4ye5geUE.png" alt="Screen Shot 2021-02-04 at 22.49.43.png" /></p>
<h2 id="deploy-your-own-portfolio-on-vercel">Deploy your own Portfolio on Vercel</h2>
<p>If you want to customize your portfolio colours, name, bio a put your Google Analytics code, I suggest you create your portfolio from <a target="_blank" href="https://devcover.vercel.app">Devcover</a> website.</p>
<p>If you prefer to go faster and accept the default parameters you can click on the following button and the only thing you have to do is put your <a target="_blank" href="https://github.com">Github</a> or <a target="_blank" href="https://hashnode.com">Hashnode</a> username in the <strong>NEXT_PUBLIC_USERNAME</strong>.</p>
<p><a target="_blank" href="https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fjrgarciadev%2Fdev-cover&amp;env=NEXT_PUBLIC_USERNAME&amp;project-name=my-awesome-portfolio&amp;repository-name=my-awesome-portfolio&amp;envDescription=&amp;envDescription=Enter%20your%20Github%20or%20Hashnode%20username%20.&amp;demo-title=APM%20Story&amp;A%20statically%20generated%portfolio%created%20using%20Devcover"><img src="https://vercel.com/button" alt="Deploy with Vercel" /></a></p>
<p>Feel free to ask in the comments or mentions me on my <a target="_blank" href="https://twitter.com/jrgarciadev">Twitter</a>.</p>
<h4 id="i-hope-this-helps-you-and-i-would-like-to-see-your-portfolio-results">I hope this helps you and I would like to see your portfolio results! 👋</h4>
]]></content:encoded></item><item><title><![CDATA[Building highly reusable React.js components using compound pattern]]></title><description><![CDATA[Today I bring you a way to create a highly reusable React component using an advance pattern called Compound.
Compound Components pattern
The keyword in the pattern’s name is the word Compound,  the word compound refers to something that is composed ...]]></description><link>https://blog.jrgarciadev.com/building-highly-reusable-reactjs-components-using-compound-pattern</link><guid isPermaLink="true">https://blog.jrgarciadev.com/building-highly-reusable-reactjs-components-using-compound-pattern</guid><category><![CDATA[Christmas Hackathon]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[patterns]]></category><dc:creator><![CDATA[Junior Garcia]]></dc:creator><pubDate>Wed, 23 Dec 2020 02:27:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1608690461912/WFAhpAzic.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today I bring you a way to create a highly reusable React component using an advance pattern called <strong>Compound</strong>.</p>
<h2 id="compound-components-pattern">Compound Components pattern</h2>
<p>The keyword in the pattern’s name is the word <strong>Compound</strong>,  the word compound refers to something that is composed of two or more separate elements.</p>
<p>With respect to React components, this could mean a component that is composed of two or more separate components. The main component is usually called the <strong>parent</strong>, and the separate composed components, <strong>children</strong>.</p>
<p>Look at the following example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1608509165705/CDiIjW3Uw.png" alt="codeimg-twitter-instream-image.png" /></p>
<p>Here, <code>&lt;Select&gt;</code> is the <strong>parent</strong> component and the <code>&lt;Select.Option&gt;</code> are children components</p>
<p>The overall behaviour of a select element also relies on having these composed option elements as well. Hence, they are connected to one another.</p>
<p>The <strong>state</strong> of the entire component is managed by <code>Select</code> component with all <code>Select.Option</code> child components dependent on that state.</p>
<p>Do you get a sense of what compound components are now?</p>
<blockquote>
<p>Compound components are just one of many ways to express the API for your components.</p>
</blockquote>
<p>We are going to build the <code>Select</code> component we saw above which will be composed of 2 additional components <code>Select Dropdown</code> and <code>Select Option</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1608513848939/NpwJIlBJK.png" alt="component-mockup.png" />
In the code block above, you’ll notice I have used expressions like this: <code>Select.Option</code></p>
<p>You can do this as well:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1608512608871/UnIIVBxt2.png" alt="codeimg-twitter-instream-image (2).png" /></p>
<p>Both work but it is a matter of personal preference. In my opinion, it communicates the dependency of the main component well, but that is just my preference.</p>
<blockquote>
<p>Feel free to use whatever component looks best to you!</p>
</blockquote>
<h2 id="building-the-compound-child-components">Building the compound child components</h2>
<p>The <code>Select</code> is our main component, will keep track of the state, and it will do this via two variables called <strong>visible</strong> (boolean) and <strong>value</strong> (string).</p>
<pre><code class="lang-js"><span class="hljs-comment">// select state </span>
{
  <span class="hljs-attr">visible</span>: <span class="hljs-literal">true</span> || <span class="hljs-literal">false</span>
  <span class="hljs-attr">value</span>: <span class="hljs-string">''</span>
}
</code></pre>
<p>The <code>Select</code> component needs to communicate the state to every child component regardless of their position in the nested component tree.</p>
<p>Remember that the children are dependent on the parent compound component for the state.</p>
<p>What would be the best way to do it?</p>
<p>We need to use the <a target="_blank" href="https://reactjs.org/docs/context.html">React Context API</a> to hold the component state and expose the <strong>visible</strong> property via the <strong>Provider</strong> component. Alongside the <strong>visible</strong> property, we will also expose a string prop to hold the selected option <strong>value</strong>.</p>
<p>We’ll be creating this in a file called <code>select-context.js</code></p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { createContext, useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">const</span> defaultContext = {
  <span class="hljs-attr">visible</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">value</span>: <span class="hljs-string">''</span>
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SelectContext = createContext(defaultContext);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useSelectContext = <span class="hljs-function">() =&gt;</span> useContext(SelectContext);
</code></pre>
<p>Now we have to create a file called <code>select-dropdown.js</code> which is the container for the select options.</p>
<blockquote>
<p>Note: I use  <a target="_blank" href="styled-components.com">Styled Components</a> for the styles, feel free to use whatever styling way looks best to you!</p>
</blockquote>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">"prop-types"</span>;
<span class="hljs-keyword">import</span> { StyledDropdown } <span class="hljs-keyword">from</span> <span class="hljs-string">"./styles"</span>;

<span class="hljs-keyword">const</span> SelectDropdown = <span class="hljs-function">(<span class="hljs-params">{ visible, children, className = <span class="hljs-string">""</span> }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledDropdown</span> <span class="hljs-attr">visible</span>=<span class="hljs-string">{visible}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledDropdown</span>&gt;</span></span>
  );
};

SelectDropdown.propTypes = {
  <span class="hljs-attr">children</span>: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
  <span class="hljs-attr">visible</span>: PropTypes.bool.isRequired,
  <span class="hljs-attr">className</span>: PropTypes.string
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SelectDropdown;
</code></pre>
<p>Next, we need to create a file called <code>styles.js</code> to save component styles.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> styled, { css } <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StyledDropdown = styled.div<span class="hljs-string">`
  position: absolute;
  border-radius: 1.375rem;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
  background-color: #fff;
  max-height: 15rem;
  width: 80vw;
  overflow-y: auto;
  overflow-anchor: none;
  padding: 1rem 0;
  opacity: <span class="hljs-subst">${(props) =&gt; (props.visible ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>)}</span>;
  visibility: <span class="hljs-subst">${(props) =&gt; (props.visible ? <span class="hljs-string">"visible"</span> : <span class="hljs-string">"hidden"</span>)}</span>;
  top: 70px;
  left: 10px;
  z-index: 1100;
  transition: opacity 0.2s, transform 0.2s, bottom 0.2s ease,
    -webkit-transform 0.2s;
`</span>;
</code></pre>
<blockquote>
<p>Note that with the <code>visible</code> property we control the visibility of the dropdown</p>
</blockquote>
<p>Then we need to create the children component,  for this, we create a file called <code>select-option.js</code>.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React, { useMemo } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useSelectContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"./select-context"</span>;
<span class="hljs-keyword">import</span> { StyledOption } <span class="hljs-keyword">from</span> <span class="hljs-string">"./styles"</span>;
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">"prop-types"</span>;

<span class="hljs-keyword">const</span> SelectOption = <span class="hljs-function">(<span class="hljs-params">{
  children,
  value: identValue,
  className = <span class="hljs-string">""</span>,
  disabled = <span class="hljs-literal">false</span>
}</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { updateValue, value, disableAll } = useSelectContext();

  <span class="hljs-keyword">const</span> isDisabled = useMemo(<span class="hljs-function">() =&gt;</span> disabled || disableAll, [
    disabled,
    disableAll
  ]);

  <span class="hljs-keyword">const</span> selected = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!value) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> value === <span class="hljs-string">"string"</span>) {
      <span class="hljs-keyword">return</span> identValue === value;
    }
  }, [identValue, value]);

  <span class="hljs-keyword">const</span> bgColor = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (isDisabled) <span class="hljs-keyword">return</span> <span class="hljs-string">"#f0eef1"</span>;
    <span class="hljs-keyword">return</span> selected ? <span class="hljs-string">"#3378F7"</span> : <span class="hljs-string">"#fff"</span>;
  }, [selected, isDisabled]);

  <span class="hljs-keyword">const</span> hoverBgColor = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (isDisabled || selected) <span class="hljs-keyword">return</span> bgColor;
    <span class="hljs-keyword">return</span> <span class="hljs-string">"#f0eef1"</span>;
  }, [selected, isDisabled, bgColor]);

  <span class="hljs-keyword">const</span> color = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (isDisabled) <span class="hljs-keyword">return</span> <span class="hljs-string">"#888888"</span>;
    <span class="hljs-keyword">return</span> selected ? <span class="hljs-string">"#fff"</span> : <span class="hljs-string">"#888888"</span>;
  }, [selected, isDisabled]);

  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    event.preventDefault();
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> updateValue === <span class="hljs-string">"function"</span> &amp;&amp; identValue !== value) {
      updateValue(identValue);
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledOption</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span>
      <span class="hljs-attr">bgColor</span>=<span class="hljs-string">{bgColor}</span>
      <span class="hljs-attr">hoverBgColor</span>=<span class="hljs-string">{hoverBgColor}</span>
      <span class="hljs-attr">color</span>=<span class="hljs-string">{color}</span>
      <span class="hljs-attr">idDisabled</span>=<span class="hljs-string">{disabled}</span>
      <span class="hljs-attr">disabled</span>=<span class="hljs-string">{disabled}</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
    &gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledOption</span>&gt;</span></span>
  );
};

SelectOption.propTypes = {
  <span class="hljs-attr">children</span>: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
  <span class="hljs-attr">value</span>: PropTypes.string,
  <span class="hljs-attr">className</span>: PropTypes.string,
  <span class="hljs-attr">disabled</span>: PropTypes.boolean
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SelectOption;
</code></pre>
<p>I know it's confused, but I’ll break it down.</p>
<p>First, let's focus on the following line of code:</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">const</span> { updateValue, value, disableAll } = useSelectContext();
</code></pre>
<p>We use <code>useSelectContext()</code> from <code>select-context.js</code> to access the context data, "⚠️Spoiler alert": we are going to manage this data on our main component, Yes you are correct is the <code>Select</code> component.</p>
<p>The <code>value</code> prop from <code>context</code> is the selected value.</p>
<p>Also, we use <code>useMemo</code> on several occasions to prevent unnecessary renders.</p>
<pre><code class="lang-js">  <span class="hljs-keyword">const</span> bgColor = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (isDisabled) <span class="hljs-keyword">return</span> <span class="hljs-string">"#f0eef1"</span>;
    <span class="hljs-keyword">return</span> selected ? <span class="hljs-string">"#3378F7"</span> : <span class="hljs-string">"#fff"</span>;
  }, [selected, isDisabled]);
</code></pre>
<p><code>useMemo</code> takes a callback that returns the <code>string</code> value with hexadecimal colour code and we pass an array dependency [selected, isDisabled]. This means that the memoized value remains the same unless the dependencies change.</p>
<blockquote>
<p>Note: If you have a theme you can use the <code>HOC</code> (High Order Component) component called <code>withTheme</code> and use your colours</p>
</blockquote>
<p>Not sure how <code>useMemo</code> works? Have a look at this  <a target="_blank" href="https://react-hooks-cheatsheet.com/usememo">cheatsheet</a>.</p>
<p>Now to finalize the <code>SelectOption</code> component we need to create the <code>StyledOption</code> component for that we go to the <code>styles.js</code> file and write the following code:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StyledOption = styled.div<span class="hljs-string">`
  display: flex;
  max-width: 100%;
  justify-content: flex-start;
  align-items: center;
  font-weight: normal;
  font-size: 1.3rem;
  height: 4rem;
  padding: 0 2rem;
  background-color: <span class="hljs-subst">${(props) =&gt; props.bgColor}</span>;
  color: <span class="hljs-subst">${(props) =&gt; props.color}</span>;
  user-select: none;
  border: 0;
  cursor: <span class="hljs-subst">${(props) =&gt; (props.isDisabled ? <span class="hljs-string">"not-allowed"</span> : <span class="hljs-string">"pointer"</span>)}</span>;
  transition: background 0.2s ease 0s, border-color 0.2s ease 0s;
  &amp;:hover {
    background-color: <span class="hljs-subst">${(props) =&gt; props.hoverBgColor}</span>;
  }
`</span>;
</code></pre>
<h2 id="creating-the-main-component">Creating the main component</h2>
<p>Up to this point, we have all the child components of our main component, now we are going to create the main component <code>Select</code>, for that we need to create a file called <code>select.js</code> with the following code: </p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React, { useState, useCallback, useMemo, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { SelectContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"./select-context"</span>;
<span class="hljs-keyword">import</span> { StyledSelect, StyledValue, StyledIcon, TruncatedText } <span class="hljs-keyword">from</span> <span class="hljs-string">"./styles"</span>;
<span class="hljs-keyword">import</span> SelectDropdown <span class="hljs-keyword">from</span> <span class="hljs-string">"./select-dropdown"</span>;
<span class="hljs-keyword">import</span> { pickChildByProps } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../utils"</span>;
<span class="hljs-keyword">import</span> { ChevronDown } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-iconly"</span>;
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">"prop-types"</span>;

<span class="hljs-keyword">const</span> Select = <span class="hljs-function">(<span class="hljs-params">{
  children,
  value: customValue,
  disabled = <span class="hljs-literal">false</span>,
  onChange,
  icon: Icon = ChevronDown,
  className,
  placeholder = <span class="hljs-string">"Choose one"</span>
}</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [visible, setVisible] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [value, setValue] = useState(<span class="hljs-literal">undefined</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (customValue === <span class="hljs-literal">undefined</span>) <span class="hljs-keyword">return</span>;
    setValue(customValue);
  }, [customValue]);

  <span class="hljs-keyword">const</span> updateVisible = useCallback(<span class="hljs-function">(<span class="hljs-params">next</span>) =&gt;</span> {
    setVisible(next);
  }, []);

  <span class="hljs-keyword">const</span> updateValue = useCallback(
    <span class="hljs-function">(<span class="hljs-params">next</span>) =&gt;</span> {
      setValue(next);
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> onChange === <span class="hljs-string">"function"</span>) {
        onChange(next);
      }
      setVisible(<span class="hljs-literal">false</span>);
    },
    [onChange]
  );

  <span class="hljs-keyword">const</span> clickHandler = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    event.preventDefault();
    <span class="hljs-keyword">if</span> (disabled) <span class="hljs-keyword">return</span>;
    setVisible(!visible);
  };

  <span class="hljs-keyword">const</span> initialValue = useMemo(
    <span class="hljs-function">() =&gt;</span> ({
      value,
      visible,
      updateValue,
      updateVisible,
      <span class="hljs-attr">disableAll</span>: disabled
    }),
    [visible, updateVisible, updateValue, disabled, value]
  );

  <span class="hljs-keyword">const</span> selectedChild = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [, optionChildren] = pickChildByProps(children, <span class="hljs-string">"value"</span>, value);
    <span class="hljs-keyword">return</span> React.Children.map(optionChildren, <span class="hljs-function">(<span class="hljs-params">child</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (!React.isValidElement(child)) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      <span class="hljs-keyword">const</span> el = React.cloneElement(child, { <span class="hljs-attr">preventAllEvents</span>: <span class="hljs-literal">true</span> });
      <span class="hljs-keyword">return</span> el;
    });
  }, [value, children]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SelectContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{initialValue}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledSelect</span>
        <span class="hljs-attr">disabled</span>=<span class="hljs-string">{disabled}</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span>
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{clickHandler}</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledValue</span> <span class="hljs-attr">isPlaceholder</span>=<span class="hljs-string">{!value}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TruncatedText</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"4rem"</span>&gt;</span>
            {!value ? placeholder : selectedChild}
          <span class="hljs-tag">&lt;/<span class="hljs-name">TruncatedText</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledValue</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledIcon</span> <span class="hljs-attr">visible</span>=<span class="hljs-string">{visible}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Icon</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledIcon</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">SelectDropdown</span> <span class="hljs-attr">visible</span>=<span class="hljs-string">{visible}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">SelectDropdown</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledSelect</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">SelectContext.Provider</span>&gt;</span></span>
  );
};

Select.propTypes = {
  <span class="hljs-attr">children</span>: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
  <span class="hljs-attr">disabled</span>: PropTypes.bool,
  <span class="hljs-attr">icon</span>: PropTypes.element,
  <span class="hljs-attr">value</span>: PropTypes.string,
  <span class="hljs-attr">placeholder</span>: PropTypes.string,
  <span class="hljs-attr">onChange</span>: PropTypes.func,
  <span class="hljs-attr">className</span>: PropTypes.string
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Select;
</code></pre>
<p>I will start by explaining the propTypes:</p>
<ul>
<li><code>children</code>: Are the array of <code>Select.Option</code></li>
<li><code>disabled</code>: Is used to set the disabled state in <code>Select</code> and <code>Select.Option</code></li>
<li><code>value</code>: Is the default selected value </li>
<li><code>placeholder</code>: Is used to show a text if there aren't any <code>Select.Option</code> selected.</li>
<li><code>onChange</code>: Callback to communicate when the value has changed</li>
<li><code>className</code>: Class name for <code>Select</code> component</li>
</ul>
<p>Perfect now let's focus on the <code>useState</code> React hook, it's used to manage selected value status and dropdown menu visibility</p>
<pre><code class="lang-js">  <span class="hljs-keyword">const</span> [visible, setVisible] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [value, setValue] = useState(<span class="hljs-literal">undefined</span>);
</code></pre>
<p>To set the initial value of <code>Select</code> (in case one is set), we need to use the hook <code>useEffect</code></p>
<pre><code class="lang-js">  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (customValue === <span class="hljs-literal">undefined</span>) <span class="hljs-keyword">return</span>;
    setValue(customValue);
  }, [customValue]);
</code></pre>
<blockquote>
<p>Not sure how <code>useEffect</code> works? Have a look at this  <a target="_blank" href="https://react-hooks-cheatsheet.com/useeffect">cheatsheet</a>. </p>
</blockquote>
<pre><code class="lang-js">  <span class="hljs-keyword">const</span> updateVisible = useCallback(<span class="hljs-function">(<span class="hljs-params">next</span>) =&gt;</span> {
    setVisible(next);
  }, []);

  <span class="hljs-keyword">const</span> updateValue = useCallback(
    <span class="hljs-function">(<span class="hljs-params">next</span>) =&gt;</span> {
      setValue(next);
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> onChange === <span class="hljs-string">"function"</span>) {
        onChange(next);
      }
      setVisible(<span class="hljs-literal">false</span>);
    },
    [onChange]
  );
</code></pre>
<p>Another hooks we are using is <code>useCallback</code>, this hook will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).</p>
<p>useCallback(fn, deps) is equivalent to useMemo(() =&gt; fn, deps).</p>
<blockquote>
<p>Not sure how <code>useCallback</code> works? Have a look at this  [cheatsheet]https://react-hooks-cheatsheet.com/usecallback). </p>
</blockquote>
<p>Now we are going to focus on context initial value, let's see following code: </p>
<pre><code class="lang-jsx">  <span class="hljs-keyword">const</span> initialValue = useMemo(
    <span class="hljs-function">() =&gt;</span> ({
      value,
      visible,
      updateValue,
      updateVisible,
      <span class="hljs-attr">disableAll</span>: disabled
    }),
    [visible, updateVisible, updateValue, disabled, value]
  );

<span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SelectContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{initialValue}</span>&gt;</span>
     // ---- ///
    <span class="hljs-tag">&lt;/<span class="hljs-name">SelectContext.Provider</span>&gt;</span></span>
  );
</code></pre>
<p>In the above code, we use the <code>useMemo</code> to prevent unnecessary re-renders passing in the array the props that can change, then we pass that initial value to the<code>SelectContect.Provider</code>, we have been using each of these properties in the components we saw earlier.</p>
<p>Last but not least, we have a function to get selected option component, let's see following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> pickChildByProps = <span class="hljs-function">(<span class="hljs-params">children, key, value</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> target = [];
  <span class="hljs-keyword">const</span> withoutPropChildren = React.Children.map(children, <span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!React.isValidElement(item)) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">if</span> (!item.props) <span class="hljs-keyword">return</span> item;
    <span class="hljs-keyword">if</span> (item.props[key] === value) {
      target.push(item);
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
    <span class="hljs-keyword">return</span> item;
  });

  <span class="hljs-keyword">const</span> targetChildren = target.length &gt;= <span class="hljs-number">0</span> ? target : <span class="hljs-literal">undefined</span>;

  <span class="hljs-keyword">return</span> [withoutPropChildren, targetChildren];
};

 <span class="hljs-keyword">const</span> selectedChild = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [, optionChildren] = pickChildByProps(children, <span class="hljs-string">"value"</span>, value);
    <span class="hljs-keyword">return</span> React.Children.map(optionChildren, <span class="hljs-function">(<span class="hljs-params">child</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (!React.isValidElement(child)) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      <span class="hljs-keyword">const</span> el = React.cloneElement(child, { <span class="hljs-attr">preventAllEvents</span>: <span class="hljs-literal">true</span> });
      <span class="hljs-keyword">return</span> el;
    });
  }, [value, children]);
</code></pre>
<p>In a few words, what we do is clone the selected option and put it in the header of the <code>Select</code> component.</p>
<p>Now we need to create the necessary styles for the <code>Select</code> component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StyledSelect = styled.div<span class="hljs-string">`
  position: relative;
  z-index: 100;
  display: inline-flex;
  align-items: center;
  user-select: none;
  white-space: nowrap;
  cursor: <span class="hljs-subst">${(props) =&gt; (props.disabled ? <span class="hljs-string">"not-allowed"</span> : <span class="hljs-string">"pointer"</span>)}</span>;
  width: 80vw;
  transition: border 0.2s ease 0s, color 0.2s ease-out 0s,
    box-shadow 0.2s ease 0s;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
  border: 2px solid #f5f5f5;
  border-radius: 3rem;
  height: 4rem;
  padding: 0 1rem 0 1rem;
  background-color: <span class="hljs-subst">${(props) =&gt; (props.disabled ? <span class="hljs-string">"#f0eef1"</span> : <span class="hljs-string">"#fff"</span>)}</span>;
  &amp;:hover {
    border-color: <span class="hljs-subst">${(props) =&gt; (props.disabled ? <span class="hljs-string">"#888888"</span> : <span class="hljs-string">"#3378F7"</span>)}</span>;
  }
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StyledIcon = styled.div<span class="hljs-string">`
  position: absolute;
  right: 2rem;
  font-size: <span class="hljs-subst">${(props) =&gt; props.size}</span>;
  top: 50%;
  bottom: 0;
  transform: translateY(-50%)
    rotate(<span class="hljs-subst">${(props) =&gt; (props.visible ? <span class="hljs-string">"180"</span> : <span class="hljs-string">"0"</span>)}</span>deg);
  pointer-events: none;
  transition: transform 200ms ease;
  display: flex;
  align-items: center;
  color: #999999;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StyledValue = styled.div<span class="hljs-string">`
  display: inline-flex;
  flex: 1;
  height: 100%;
  align-items: center;
  line-height: 1;
  padding: 0;
  margin-right: 1.25rem;
  font-size: 1.3rem;
  color: "#888888";
  width: calc(100% - 1.25rem);
  <span class="hljs-subst">${StyledOption}</span> {
    border-radius: 0;
    background-color: transparent;
    padding: 0;
    margin: 0;
    color: inherit;
    &amp;:hover {
      border-radius: inherit;
      background-color: inherit;
      padding: inherit;
      margin: inherit;
      color: inherit;
    }
  }
  <span class="hljs-subst">${({ isPlaceholder }</span>) =&gt;
    isPlaceholder &amp;&amp;
    css`</span>
      <span class="hljs-attr">color</span>: #bcbabb;
    <span class="hljs-string">`}
`</span>;
</code></pre>
<blockquote>
<p>Feel free to change the colours, you can use the <code>theme</code> object of <code>styled-components</code> to get the theme colours</p>
</blockquote>
<p>Finally, we need to export our component 👏🏻 </p>
<pre><code class="lang-js">
<span class="hljs-keyword">import</span> Select <span class="hljs-keyword">from</span> <span class="hljs-string">"./select"</span>;
<span class="hljs-keyword">import</span> SelectOption <span class="hljs-keyword">from</span> <span class="hljs-string">"./select-option"</span>;

<span class="hljs-comment">// Remember this is just a personal preference. It's not mandatory</span>
Select.Option = SelectOption;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Select;
</code></pre>
<p>Congratulations! 🎊, now you have a reusable highly optimized component created, you can apply this pattern in many cases.</p>
<h2 id="final-result">Final result</h2>
<p>Here yo can see the final result:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/react-compound-pattern-73rx6?fontsize=14&amp;hidenavigation=1&amp;theme=dark">https://codesandbox.io/embed/react-compound-pattern-73rx6?fontsize=14&amp;hidenavigation=1&amp;theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="react-compound-pattern"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts</a></div>
]]></content:encoded></item><item><title><![CDATA[How to create your own image CDN with Thumbor and AWS]]></title><description><![CDATA[Image CDNs like  Cloudinary  or  Imgix make it easy to dynamically optimize the aesthetics and performance of your images. Thumbor can help us to do the same but with a difference, is open-source and can be used for free to resize, apply filters, com...]]></description><link>https://blog.jrgarciadev.com/how-to-create-your-own-image-cdn-with-thumbor-and-aws</link><guid isPermaLink="true">https://blog.jrgarciadev.com/how-to-create-your-own-image-cdn-with-thumbor-and-aws</guid><category><![CDATA[image]]></category><category><![CDATA[AWS]]></category><category><![CDATA[CDN]]></category><category><![CDATA[command line]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Junior Garcia]]></dc:creator><pubDate>Sun, 06 Dec 2020 23:31:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1607297470419/fBfFh9GI6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Image CDNs like  <a target="_blank" href="https://cloudinary.com/">Cloudinary</a>  or  <a target="_blank" href="https://www.imgix.com/">Imgix</a> make it easy to dynamically optimize the aesthetics and performance of your images. <a target="_blank" href="http://thumbor.org/">Thumbor</a> can help us to do the same but with a difference, is open-source and can be used for free to resize, apply filters, compress, and transform images. It's currently used by <a target="_blank" href="https://wikitech.wikimedia.org/wiki/Thumbor">Wikipedia</a> and  <a target="_blank" href="https://99designs.com/blog/engineering/on-demand-image-thumbnailing-with-thumbor/">99Designs</a>.</p>
<blockquote>
<p>Note: This tutorial is based on 
Katie Hempenius article about <a target="_blank" href="https://web.dev/install-thumbor/">How to install thumbor</a> but adding AWS EC2 and S3.</p>
</blockquote>
<p>This post explains how to install Thumbor on your own server. Once installed, you can use Thumbor as an API to transform your images, we will install on AWS EC2 server and integrate it with AWS S3 to deliver the images as quickly as possible and save the transformations.</p>
<blockquote>
<p>This post assumes that you have an AWS account and how to use command-line tools to set up the VM.</p>
</blockquote>
<p>First of all, we are going to launch an EC2 VM with Ubuntu 18.04LTS 64-bit (x86), I used t2 micro because it's free tier but you can choose the one that best suits your needs.</p>
<h3 id="install-thumbor-dependencies">Install Thumbor dependencies</h3>
<p>Update and upgrade Ubuntu's already-installed packages:</p>
<pre><code>sudo apt-<span class="hljs-keyword">get</span> <span class="hljs-keyword">update</span> -y &amp;&amp; sudo apt-<span class="hljs-keyword">get</span> upgrade -y
</code></pre><p>Now we need to install <code>pip</code>, the package manager for Python. Later you'll install Thumbor with <code>pip</code>. </p>
<pre><code>sudo apt-<span class="hljs-keyword">get</span> install -y python-pip
</code></pre><p>Install Thumbor's dependencies.</p>
<pre><code><span class="hljs-meta">#ssl packages</span>
sudo apt-<span class="hljs-keyword">get</span> install -y libcurl4-openssl-dev libssl-dev
</code></pre><pre><code><span class="hljs-meta">#computer vision packages</span>
sudo apt-<span class="hljs-keyword">get</span> install -y python-opencv libopencv-dev
</code></pre><pre><code><span class="hljs-meta">#image format packages</span>
sudo apt-<span class="hljs-keyword">get</span> install -y libjpeg-dev libpng-dev libwebp-dev webp
</code></pre><h3 id="install-thumbor">Install Thumbor</h3>
<pre><code><span class="hljs-attribute">sudo</span> pip install thumbor
</code></pre><p>To see if Thumbor installed correctly this should work:</p>
<pre><code>thumbor <span class="hljs-comment">--help</span>
</code></pre><p>Hurrah! 🥳</p>
<p>Now we are going to start the <code>debug</code> server of Thumbor to make some requests and test it.</p>
<pre><code><span class="hljs-attribute">thumbor</span> --log-level <span class="hljs-literal">debug</span>
</code></pre><h3 id="aws-ec2-settings">AWS EC2 Settings</h3>
<p>Thumbor is now running.</p>
<p>By default, Thumbor runs on port 8888 However, this probably won't work for you (yet) because cloud providers usually require that you explicitly open firewall ports before they will accept incoming traffic, so we are going to open required ports on our Amazon EC2 instance.</p>
<p>To open the required ports we have to select on the left menu <strong>Network &amp; Security</strong> -&gt; <strong>Security Groups</strong>, we select the group assigned to our EC2 instance usually called <strong>launch-wizard-1</strong> -&gt; <strong>Actions</strong> -&gt; <strong>Edit Inbound Rules</strong>, 
once there we create the rules defined below and we press <strong>Save rules</strong>.</p>
<table>
<thead>
<tr>
<td>Type</td><td>Protocol</td><td>Port range</td><td>Source</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td>HTTP</td><td>TCP</td><td>80</td><td>Anywhere</td><td>http port</td></tr>
<tr>
<td>Custom TCP</td><td>TCP</td><td>8888</td><td>Anywhere</td><td>thumbor port</td></tr>
<tr>
<td>SSH</td><td>TCP</td><td>22</td><td>Anywhere</td><td>to accept ssh connections</td></tr>
<tr>
<td>HTTPS</td><td>TCP</td><td>443</td><td>Anywhere</td><td>https port</td></tr>
</tbody>
</table>
<p>Once created our security rules, we should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1607206569873/YHXoU1HAL.png" alt="Screen Shot 2020-12-05 at 19.16.04.png" /></p>
<h3 id="try-it-out">Try It Out</h3>
<p>Thumbor is now accessible and ready for use 🥳👏🏻. Try it out by visiting the following URL:</p>
<p><code>http://YOUR_VIRTUAL_MACHINE:8888/unsafe/100x100/https://jrgarciadev.com/avatar.png</code></p>
<p>You should see an image that is 100 pixels wide by 100 pixels tall. Thumbor has taken the image avatar.png and size specified in the URL string and served the result. You can replace the image in the URL string (i.e., https://jrgarciadev.com/avatar.png) with any other image (e.g., https://your-site.com/kitten.jpg) and Thumbor will resize that image too.</p>
<blockquote>
<p>Note: Thumbor uses HTTP by default but can be <a target="_blank" href="https://thumbor.readthedocs.io/en/latest/image_loader.html">configured</a>  to use HTTPS.</p>
</blockquote>
<p>I recommend you visit this article <a target="_blank" href="https://web.dev/use-thumbor/#thumbor-url-format">The Optimize images with Thumbor</a>  for more information on using the Thumbor API and Thumbor configuration file.</p>
<h3 id="configuring-thumbor-to-run-in-background">Configuring Thumbor to run in background</h3>
<p>In a production environment, we need to configure Thumbor to start automatically 
to do this we are going to make use of Linux <a target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.html">Systemd</a>. </p>
<p>Navigate to the <code>/lib/systemd/system directory</code>. This directory contains the service files for <code>systemd</code>.</p>
<pre><code>cd /lib/systemd/<span class="hljs-keyword">system</span>
</code></pre><p>As superuser, create a <code>thumbor. service</code> file.</p>
<pre><code><span class="hljs-selector-tag">sudo</span> <span class="hljs-selector-tag">touch</span> <span class="hljs-selector-tag">thumbor</span><span class="hljs-selector-class">.service</span>
</code></pre><p>Using vim or nano add the following configuration to Thumbor service. This configuration will run <code>/usr/local/bin/thumbor</code> (i.e. the Thumbor binary) once networking is available and will restart Thumbor on failure.</p>
<p>I prefer to use vim so the command is the bellow:</p>
<pre><code><span class="hljs-selector-tag">sudo</span> <span class="hljs-selector-tag">vim</span> <span class="hljs-selector-tag">thumbor</span><span class="hljs-selector-class">.service</span>
</code></pre><pre><code><span class="hljs-section">[Unit]</span>

<span class="hljs-attr">Description</span>=Service for Thumbor image CDN

<span class="hljs-attr">Documentation</span>=https://thumbor.readthedocs.io/en/latest/

<span class="hljs-attr">After</span>=network.target

<span class="hljs-section">[Service]</span>

<span class="hljs-attr">ExecStart</span>=/usr/local/bin/thumbor

<span class="hljs-attr">Restart</span>=<span class="hljs-literal">on</span>-failure

<span class="hljs-section">[Install]</span>

<span class="hljs-attr">WantedBy</span>=multi-user.target
</code></pre><p>Now we need to start Thumbor, to do this we are going to use<code>systemctl</code> which is the utility used to manage <code>systemd</code>.</p>
<pre><code>sudo systemctl <span class="hljs-keyword">start</span> thumbor.service
</code></pre><blockquote>
<p>Note: If Thumbor is currently running, you should stop it before attempting to start Thumbor using <code>systemctl</code>.</p>
</blockquote>
<p>Next, "enable" Thumbor. This means that Thumbor will automatically start on boot.</p>
<pre><code>sudo systemctl <span class="hljs-keyword">enable</span> thumbor.service
</code></pre><p>Verify that you've successfully configured <code>systemd</code> by running the status command.</p>
<pre><code><span class="hljs-selector-tag">systemctl</span> <span class="hljs-selector-tag">status</span> <span class="hljs-selector-tag">thumbor</span><span class="hljs-selector-class">.service</span>
</code></pre><p>The status should show that it is enabled and active.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1607227875940/BdBNV1ztX.png" alt="Screen Shot 2020-12-06 at 01.11.12.png" /></p>
<p>Congratulations 🎊, now you have a Thumbor service running and ready to make transformations, apply filters, resize and convert any image.</p>
<h3 id="aws-s3-configuration">AWS S3 Configuration</h3>
<p>We have running our Thumbor service successfully but the aim of this post is to guide you to use Thumbor efficiently, for this reason, we are going to save the transformations in an S3 bucket so we will only do the transformation once if we do the same request again, Thumbor will send us the image that it already has saved in the bucket. </p>
<p>Here we go! </p>
<p>We have to create <strong>2</strong> S3 buckets with all default settings, one for the data source and the other for saving the results, I have named them <code>thumbor-blog-source</code> and <code>thumbor-blog-results</code>.</p>
<p>Once we have created our S3 bucket you have to upload an image on your source S3 bucket only for test, then return the server terminal and we are going to execute the following command to create a new <code>thumbor.conf</code> file:</p>
<pre><code>thumbor-config &gt; ./thumbor.conf
</code></pre><p>Now we have to install a <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html#configuration">Boto3</a> to management the AWS authentication and <a target="_blank" href="https://github.com/thumbor-community/aws">AWS S3 Plugin</a> just run next commands:</p>
<pre><code><span class="hljs-attribute">pip</span> install boto<span class="hljs-number">3</span>
</code></pre><p>Once boto3 is installed we need to configure our AWS S3 connection to do this are going to create a file for boto3 configuration:</p>
<pre><code><span class="hljs-keyword">mkdir</span> .aws &amp;&amp; sudo touch .aws/config &amp;&amp; sudo touch .aws/credentials
</code></pre><p>Open the <code>.aws/config</code> file a write the next content:</p>
<pre><code><span class="hljs-section">[default]</span>
<span class="hljs-attr">region</span>=YOUR_S3_REGION
</code></pre><p>Save it and open the <code>.aws/credentials</code> file and write:</p>
<pre><code><span class="hljs-section">[default]</span>
<span class="hljs-attr">aws_access_key_id</span> = YOUR_ACCESS_KEY
<span class="hljs-attr">aws_secret_access_key</span> = YOUR_SECRET_KEY
</code></pre><blockquote>
<p>Note: <code>YOUR_S3_REGION</code> by default is <code>us-east-1</code> or <code>us-east-2</code></p>
</blockquote>
<p>Then we have to install <a target="_blank" href="https://github.com/thumbor-community/aws">Thumbor AWS</a></p>
<pre><code> <span class="hljs-attribute">pip</span> install tc_aws
</code></pre><p>Open your <code>thumbor.conf</code> file created earlier, there are a lot of options but for now, we are going to focus only on the TC_AWS section (This section doesn’t exist in the default config and you’ll need to add it somewhere).</p>
<pre><code><span class="hljs-comment">############################## TC_AWS ##########################################</span>
<span class="hljs-attr">TC_AWS_REGION</span> = <span class="hljs-string">'YOUR_AWS_REGION'</span> <span class="hljs-comment"># AWS Region</span>

<span class="hljs-attr">TC_AWS_ENDPOINT</span> = None <span class="hljs-comment"># Custom S3 endpoint URL (for GCP, Minio, etc.)</span>

<span class="hljs-attr">TC_AWS_STORAGE_BUCKET</span> = <span class="hljs-string">'YOUR_SOURCE_S3_BUCKET_NAME'</span> <span class="hljs-comment"># S3 bucket for Storage</span>
<span class="hljs-attr">TC_AWS_STORAGE_ROOT_PATH</span> = <span class="hljs-string">''</span> <span class="hljs-comment"># S3 path prefix for Storage bucket</span>

<span class="hljs-attr">TC_AWS_LOADER_BUCKET</span> = <span class="hljs-string">'YOUR_SOURCE_S3_BUCKET_NAME'</span> <span class="hljs-comment">#S3 bucket for loader</span>
<span class="hljs-attr">TC_AWS_LOADER_ROOT_PATH</span> = <span class="hljs-string">''</span> <span class="hljs-comment"># S3 path prefix for Loader bucket</span>

<span class="hljs-attr">TC_AWS_RESULT_STORAGE_BUCKET</span> = <span class="hljs-string">'YOUR_RESULT_S3_BUCKET_NAME'</span> <span class="hljs-comment"># S3 bucket for result Storage</span>
<span class="hljs-attr">TC_AWS_RESULT_STORAGE_ROOT_PATH</span> = <span class="hljs-string">'thumbor_results'</span> <span class="hljs-comment"># S3 path prefix for Result storage bucket</span>

<span class="hljs-comment"># put data into S3 using the Server Side Encryption functionality to</span>
<span class="hljs-comment"># encrypt data at rest in S3</span>
<span class="hljs-comment"># https://aws.amazon.com/about-aws/whats-new/2011/10/04/amazon-s3-announces-server-side-encryption-support/</span>
<span class="hljs-attr">TC_AWS_STORAGE_SSE</span> = <span class="hljs-literal">False</span>

<span class="hljs-comment"># put data into S3 with Reduced Redundancy</span>
<span class="hljs-comment"># https://aws.amazon.com/about-aws/whats-new/2010/05/19/announcing-amazon-s3-reduced-redundancy-storage/</span>
<span class="hljs-attr">TC_AWS_STORAGE_RRS</span> = <span class="hljs-literal">False</span>


<span class="hljs-comment"># Enable HTTP Loader as well?</span>
<span class="hljs-comment"># This would allow you to load watermarks in over your images dynamically through a URI</span>
<span class="hljs-comment"># E.g.</span>
<span class="hljs-comment"># http://your-thumbor.com/unsafe/filters:watermark(http://example.com/watermark.png,0,0,50)/s3_bucket/photo.jpg</span>
<span class="hljs-attr">TC_AWS_ENABLE_HTTP_LOADER</span> = <span class="hljs-literal">False</span>

<span class="hljs-attr">TC_AWS_ALLOWED_BUCKETS</span> = <span class="hljs-literal">False</span> <span class="hljs-comment"># List of the allowed bucket to be requested</span>
<span class="hljs-attr">TC_AWS_STORE_METADATA</span> = <span class="hljs-literal">False</span> <span class="hljs-comment"># Store result with metadata (for instance content-type)</span>
<span class="hljs-comment">################################################################################</span>
</code></pre><p>Now we have to uncomment and edit these lines on the <code>thumbor.conf</code> file:</p>
<pre><code><span class="hljs-comment">## Defaults to: 'thumbor.loaders.http_loader'</span>
<span class="hljs-attr">LOADER</span> = <span class="hljs-string">'tc_aws.loaders.s3_loader'</span>

<span class="hljs-comment">## Defaults to: 'thumbor.storages.file_storage'</span>
<span class="hljs-attr">STORAGE</span> = <span class="hljs-string">'thumbor.storages.no_storage'</span>

<span class="hljs-comment">## Defaults to: None</span>
<span class="hljs-attr">RESULT_STORAGE</span> = <span class="hljs-string">'tc_aws.result_storages.s3_storage'</span>
</code></pre><p>And finally, we have to set as <code>RESULT_STORAGE_STORES_UNSAFE = True</code> option to save thumbor results in our results bucket.</p>
<pre><code><span class="hljs-comment">######</span><span class="hljs-comment">######</span><span class="hljs-comment">######</span><span class="hljs-comment">######</span><span class="hljs-comment">######</span><span class="hljs-comment">## Result Storage ################################</span>

<span class="hljs-comment">## Indicates whether unsafe requests should also be stored in the Result Storage</span>
<span class="hljs-comment">## Defaults to: False</span>
RESULT_STORAGE_STORES_UNSAFE = True
</code></pre><p>Then we need to restart the Thumbor service so that it takes the new settings, write the next command on your server command line:</p>
<pre><code>sudo systemctl <span class="hljs-keyword">restart</span> thumbor.service
</code></pre><p>Execute the <code>status</code> command to be sure that it's running correctly</p>
<blockquote>
<p>Note: If for any reason the server is not running (<code>sudo systemctl stop thumbor.service</code>) you can stop it and try out with debug mode with <code>thumbor --log-level debug</code></p>
</blockquote>
<p>Now visit the URL of your server with the name of the image saved on the source bucket, in my case <code>thumbor-blog-source</code></p>
<p>Here a diagram of what we have configured in thumbor :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1607236942814/xONC4WmmLy.jpeg" alt="thumbor-blog.jpg" /></p>
<p>I'm proud that you made it this far 👏🏻</p>
<p>If you liked it, just let me know and so I will continue with part 2 of this post, where we will delve into the Thumbor options for image optimization, security and we will also install an Nginx server and connect it with CloudFront, an authentic CDN of images! 🚀</p>
<p>Extra: We can see how to mount 4 Thumbor servers managed by an AWS Load Balancer.</p>
<p>Thanks for reading! 🙋🏻‍♂️</p>
]]></content:encoded></item><item><title><![CDATA[Beautiful and pixel perfect React Icon Library]]></title><description><![CDATA[React Iconly - Based on  Iconly  v2 icons
One of the phases in which we invest more time when we are developing or designing a web or mobile application is in choosing the icon pack that best suits the brand and what we want to convey, even creating ...]]></description><link>https://blog.jrgarciadev.com/beautiful-and-pixel-perfect-react-icon-library</link><guid isPermaLink="true">https://blog.jrgarciadev.com/beautiful-and-pixel-perfect-react-icon-library</guid><category><![CDATA[React]]></category><category><![CDATA[npm]]></category><category><![CDATA[software development]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Junior Garcia]]></dc:creator><pubDate>Mon, 30 Nov 2020 20:48:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606771089541/-cPKZ5vg3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>React Iconly</strong> - Based on  <a target="_blank" href="https://ui8.net/piqodesign/products/iconly-essential-icons">Iconly</a>  v2 icons</p>
<p>One of the phases in which we invest more time when we are developing or designing a web or mobile application is in choosing the <strong>icon pack</strong> that best suits the brand and what we want to convey, even creating our own <strong>set</strong> of icons if none of them fits or we want it to be unique and identify your brand.</p>
<p> <a target="_blank" href="https://ui8.net/piqodesign/products/iconly-essential-icons?spm=a2c6h.14275010.0.0.541d5debaFCnO5">Iconly</a>  is one of the options that is being used by designers and developers today, so I decided to create a library for React / Next.js / Gatsby that facilitates its use and that also allows us to customize any icon according to our needs.</p>
<p>On the <a target="_blank" href="https://react-iconly.jrgarciadev.com">Website</a>, you can find all the icons sorted by sets</p>
<p>Integrating into your project is quite simple just by following these steps:</p>
<h3 id="installation">Installation</h3>
<pre><code>yarn <span class="hljs-keyword">add</span> react-iconly
</code></pre><p>or</p>
<pre><code><span class="hljs-built_in">npm</span> install react-iconly
</code></pre><h3 id="usage">Usage</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { Home } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-iconly'</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span></span>
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>You can also wrap your app inside an <code>IconlyProvider</code> component, this will make all the icons that are within the context use the Provider properties</p>
<p>If you set specific props for each Icon the Provider properties will be overwritten</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { IconlyProvider, Home, Notification } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-iconly'</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">IconlyProvider</span> <span class="hljs-attr">set</span>=<span class="hljs-string">"bulk"</span> <span class="hljs-attr">primaryColor</span>=<span class="hljs-string">"blueviolet"</span> <span class="hljs-attr">secondaryColor</span>=<span class="hljs-string">"blue"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"bold"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"xlarge"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Notification</span> <span class="hljs-attr">primaryColor</span>=<span class="hljs-string">"yellow"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">IconlyProvider</span>&gt;</span></span>
  )
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Icons can be configured with inline props:</p>
<pre><code class="lang-jsx">&lt;Home set=<span class="hljs-string">"two-tone"</span> primaryColor=<span class="hljs-string">"blueviolet"</span> secondaryColor=<span class="hljs-string">"blue"</span> stroke=<span class="hljs-string">"bold"</span> size=<span class="hljs-string">"xlarge"</span>/&gt;
</code></pre>
<p>You can also include the whole icon pack:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> Iconly <span class="hljs-keyword">from</span> <span class="hljs-string">'react-iconly'</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Iconly.Home</span> <span class="hljs-attr">set</span>=<span class="hljs-string">"bulk"</span> <span class="hljs-attr">primaryColor</span>=<span class="hljs-string">"blueviolet"</span> <span class="hljs-attr">secondaryColor</span>=<span class="hljs-string">"blue"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"bold"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"xlarge"</span>/&gt;</span></span>
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Custom style example</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { Send } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-iconly'</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Send</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">transform:</span> '<span class="hljs-attr">rotate</span>(<span class="hljs-attr">45deg</span>)' }} <span class="hljs-attr">primaryColor</span>=<span class="hljs-string">"red"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"bold"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"xlarge"</span>/&gt;</span></span>
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>For more instructions visit:</p>
<p><a target="_blank" href="https://github.com/jrgarciadev/react-iconly">Github repository</a> </p>
<p>Thanks for reading!</p>
]]></content:encoded></item></channel></rss>