<?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: Georgina Grey</title>
    <description>The latest articles on DEV Community by Georgina Grey (@georginagrey).</description>
    <link>https://dev.to/georginagrey</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%2F85270%2F1bb07abc-fe91-4a4f-a573-fd15ff3b9421.jpeg</url>
      <title>DEV Community: Georgina Grey</title>
      <link>https://dev.to/georginagrey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/georginagrey"/>
    <language>en</language>
    <item>
      <title>Share your best Serverless resources (please? :) )</title>
      <dc:creator>Georgina Grey</dc:creator>
      <pubDate>Sat, 31 Aug 2019 02:05:31 +0000</pubDate>
      <link>https://dev.to/georginagrey/share-your-best-serverless-resources-please-12b1</link>
      <guid>https://dev.to/georginagrey/share-your-best-serverless-resources-please-12b1</guid>
      <description>&lt;p&gt;Hi dev.to!&lt;/p&gt;

&lt;p&gt;I have an upcoming challenge involving everything Serverless and I'm looking to compile a list of resources for future reference. I'm looking for material beyond basic tutorials and more focused on architecture and multi-cloud.&lt;/p&gt;

&lt;p&gt;I mean: who to follow? Maybe books? Courses, blog posts, etc.&lt;/p&gt;

&lt;p&gt;For example today I found James Beswick's &lt;a href="https://medium.com/@jbesw"&gt;posts&lt;/a&gt; which I find super interesting and helpful since he covers other topics beyond "how to build this and that".&lt;/p&gt;

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

</description>
      <category>serverless</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>How I leveraged JAMstack to cut down costs for a nonprofit</title>
      <dc:creator>Georgina Grey</dc:creator>
      <pubDate>Sat, 19 Jan 2019 03:39:14 +0000</pubDate>
      <link>https://dev.to/georginagrey/how-i-leveraged-jamstack-to-cut-down-costs-for-a-nonprofit-76f</link>
      <guid>https://dev.to/georginagrey/how-i-leveraged-jamstack-to-cut-down-costs-for-a-nonprofit-76f</guid>
      <description>&lt;p&gt;Hi dev.to!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last year I was hired to build out a website for a nonprofit feminist collective. I chose GatsbyJS+Netlify to drastically cut down costs.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell me what you want, what you really really want 🎤
&lt;/h2&gt;

&lt;p&gt;When dealing with nonprofits, costs are always something to consider for obvious reasons, money is hard to come by and a stream of cash is not always guaranteed. &lt;/p&gt;

&lt;p&gt;For this website the girls required to do some blogging, event publishing with image galleries and most importantly, build a support network (in this case a directory) for women entrepreneurs to sign up and have their business listed on the platform. So we're talking hosting, CMS integration, forms, image+data storage and confirmation emails.&lt;/p&gt;

&lt;p&gt;As developers we tend to get overly excited about trying and learning new things, and our clients aren't any different, they're always buzzing with ideas and opinions and a tight budget, here's where we need to meet halfway.&lt;/p&gt;

&lt;p&gt;So, let's balance things out, as a developer, it's important for me to cut down on the time I spend coding the site to avoid increasing my prices hence making it less possible for a nonprofit to afford my services.&lt;/p&gt;

&lt;h3&gt;
  
  
  I can be your hero baby! — &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;The JAMstack&lt;/a&gt; 😘
&lt;/h3&gt;

&lt;p&gt;After sorting out the requirements, I started to make a list of the tools/frameworks/platforms available that overlapped with our goals.&lt;/p&gt;

&lt;p&gt;✔️ &lt;strong&gt;GatsbyJS&lt;/strong&gt; - static rendering. Just upload the files to a CDN service and you're good to go. Good for me since I already knew React and I would get to learn Graphql (I was super overdue on this one).&lt;br&gt;
✔️ &lt;strong&gt;Netlify&lt;/strong&gt; - free hosting and SSL. Yep, hosting has gotten so cheap in recent years, they don't ever bother on charging it anything anymore, they even get you a SSL certificate for free to use with your own domain. This bad boy will make you forget about &lt;em&gt;DevOps&lt;/em&gt; while giving you a continuous deployment pipeline (you can test features on different branches before publishing). Messed up the build? No problem, Netlify keeps a history of every build and won't serve if it's broken.&lt;br&gt;
✔️ &lt;strong&gt;Github&lt;/strong&gt; - for the independent headless blogging who needs no database 💅. We just need to point Netlify to our Github repo and it will take care of the rest. We don't need to spend our precious $ on saving information to a database is meant to be public anyway. &lt;br&gt;
✔️ &lt;strong&gt;NetlifyCMS&lt;/strong&gt; - free headless CMS. There are plenty of tutorials and a Gatsby starter to get the ball rolling. Is simple and intuitive, but powerful at the same time, is built with React so I was able to create custom widgets and fields and make it super tailored the girls needs.&lt;br&gt;
✔️ &lt;strong&gt;Firebase functions&lt;/strong&gt; - confirmation emails. When a user registers their business they get a conformation email and once it's reviewed and accepted will get another one, to sort this out I used NodeJS with Nodemailer to deploy a couple of functions to Google Firebase. Inside those functions just setup a couple of triggers on Firestore's collections and voilà.&lt;/p&gt;

&lt;h3&gt;
  
  
  GatsbyJS beyond the Blog
&lt;/h3&gt;

&lt;p&gt;I've been following Gatsby's roadmap for some time now, I try to attend every &lt;a href="https://www.gatsbyjs.com/webinars/" rel="noopener noreferrer"&gt;webinar&lt;/a&gt; to learn more about current and future updates, is actually really exciting how they plan to go beyond just static rendering. PS. I think I'm fangirling now 😅&lt;/p&gt;

&lt;p&gt;In the JAMstack world is not all rainbows and butterflies, depending on the project there could be some constraints and it might not be a great fit, but is worth giving it a go when there's a match.&lt;/p&gt;

&lt;h2&gt;
  
  
  There ain't no such thing as a free lunch 😒
&lt;/h2&gt;

&lt;p&gt;I also did some other API integrations that are not &lt;em&gt;entirely&lt;/em&gt; free, such as Firestore, Firebase functions and Cloudinary, but their free plans are very generous, they will suffice for small websites such as this one. When choosing a third party API, be cautious, since sometimes can be tricky to estimate costs correctly.&lt;/p&gt;

&lt;p&gt;Most people ignore the hidden costs of using website builders like WordPress, yes it's true you can get up and running with minimal investment, but they fail to account for maintenance costs such as upgrades, backups and general cleanup of plugins, broken links, etc.&lt;/p&gt;

&lt;p&gt;These costs can be reflected in different ways, either the time it takes you to learn how to properly maintain it, hiring a developer do to it for you or ignoring it until something fails and emergency mesures need to be taken (when 💩 hits the fan 💸).&lt;/p&gt;

&lt;h2&gt;
  
  
  Party time! 🙌
&lt;/h2&gt;

&lt;p&gt;In this case study for &lt;a href="https://chicasalfrente.com" rel="noopener noreferrer"&gt;&lt;em&gt;Chicas al Frente (Girls in Front)&lt;/em&gt;&lt;/a&gt;, we saved a significant amount of money in hosting and maintenance costs without sacrificing quality, in the contrary, they get to enjoy the benefits of static sites and GatsbyJS in particular (like PWA), such as better performance and security; plus Netlify's CDN delivery, hosting, security and CMS.&lt;/p&gt;

&lt;p&gt;As a developer working with this stack for the first time, I have to say it was a very pleasant experience, you do have to get around some limitations but that is expected. Not having to worry about dealing with recurrent software upgrades and security patches, is also very nice 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fgeorginagrey%2Fimage%2Fupload%2Fv1547005915%2Fref2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fgeorginagrey%2Fimage%2Fupload%2Fv1547005915%2Fref2.png" alt="www.chicasalfrente.com"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>showdev</category>
      <category>jamstack</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Show off your JAMstack projects</title>
      <dc:creator>Georgina Grey</dc:creator>
      <pubDate>Sat, 13 Oct 2018 16:33:49 +0000</pubDate>
      <link>https://dev.to/georginagrey/show-off-your-jamstack-projects-2pbc</link>
      <guid>https://dev.to/georginagrey/show-off-your-jamstack-projects-2pbc</guid>
      <description>&lt;p&gt;Hi dev.to! &lt;/p&gt;

&lt;p&gt;It wasn't until a couple of months back that I started paying attention to the "JAMstack" trend, if you're not familiar with the term, check out this post:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/lewismenelaws" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2-afuk5u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--f0IVuKD7--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/10181/d4fbfbdb-8237-4b08-acef-e23862a2a516.jpg" alt="lewismenelaws image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/lewismenelaws/jamstack-how-sites-are-becoming-even-faster-in-a-bloated-web-fig" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;JAMstack. How sites are becoming even faster in a bloated web.&lt;/h2&gt;
      &lt;h3&gt;Lewis Menelaws ・ Oct 12 '18 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#jamstack&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Like every other technology it comes with some limitations, so I'm curious to see what type of projects you have built using the JAMstack and how you came around those constraints (if any).&lt;/p&gt;

&lt;p&gt;I hope to share mine soon :)&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>jamstack</category>
      <category>static</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My first portfolio with React and AWS</title>
      <dc:creator>Georgina Grey</dc:creator>
      <pubDate>Sun, 29 Jul 2018 01:58:17 +0000</pubDate>
      <link>https://dev.to/georginagrey/my-first-portfolio-with-react-and-aws-2g73</link>
      <guid>https://dev.to/georginagrey/my-first-portfolio-with-react-and-aws-2g73</guid>
      <description>&lt;p&gt;Hi dev.to! So, I built my first portfolio and thought about documenting the process, but before jumping in, a disclaimer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I believe in choosing the right tool for the job, using React for a portfolio might seem like an overkill but I decided on it mostly because I want to get better at it.&lt;/li&gt;
&lt;li&gt;For that same reason I chose AWS to deploy it instead of Github or Netlifly. AWS is a beast and I want to learn as much as I can.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phew! Okay, so let's get to it. Oh, here's the portfolio &lt;a href="https://georginagrey.com" rel="noopener noreferrer"&gt;https://georginagrey.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The interesting bits
&lt;/h2&gt;

&lt;p&gt;When coding the app I learned a few new tricks that I believe are worth sharing.&lt;/p&gt;

&lt;h3&gt;
  
  
  React's Context API
&lt;/h3&gt;

&lt;p&gt;My portfolio is multi-language, to achieve that I used &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;React's Context&lt;/a&gt;, the point is to have a sort of &lt;strong&gt;"global"&lt;/strong&gt; state that can be accessed by other components that could be deeply nested, thus avoiding passing &lt;strong&gt;props&lt;/strong&gt; many levels down the chain. This is how it helped me implement the language switcher:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provider&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;LanguageContext.js&lt;/em&gt; is where the text translations live and the &lt;em&gt;Context&lt;/em&gt; is created and exported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//LanguageContext.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;languages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LanguageContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;langText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;toggleLanguage&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;App&lt;/em&gt; component is the most outer component, where the &lt;em&gt;toggleLanguage&lt;/em&gt; function is actually implemented. &lt;em&gt;LanguageContext.Provider&lt;/em&gt; component wraps every other children that needs to consume the &lt;em&gt;"global" state&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Watch out when sharing functions that access &lt;em&gt;state&lt;/em&gt;, such functions need be &lt;em&gt;explicitly binded&lt;/em&gt; to &lt;em&gt;state&lt;/em&gt;, by either using the &lt;em&gt;super(props)&lt;/em&gt; keyword or the &lt;em&gt;bind(this)&lt;/em&gt; method, otherwise components nested deep down executing this function will throw an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LanguageContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;languages&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./LanguageContext&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="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;langText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;toggleLanguage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleLanguage&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;toggleLanguage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LanguageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Menu&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Footer&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LanguageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Consumer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;LanguagePicker&lt;/em&gt; component is nested about 3 levels deep, thanks to the &lt;em&gt;LanguageContext.Consumer&lt;/em&gt; component, this is how &lt;em&gt;state&lt;/em&gt; can be accessed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// LanguagePicker.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LanguagePicker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LanguageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;toggleLanguage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;toggleLanguage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        ...
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LanguageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could have been achieved with Redux too, but I didn't need it for anything else. The &lt;em&gt;Context API&lt;/em&gt; shouldn't be used lightly though, so keep that in mind.&lt;/p&gt;







&lt;h3&gt;
  
  
  Intersection Observer API
&lt;/h3&gt;

&lt;p&gt;It's very useful if a behavior needs to be triggered when some element is visible inside the viewport. I used it to trigger some animations, but the most meaningful use case has to do with improving the site's &lt;strong&gt;load time&lt;/strong&gt;, &lt;strong&gt;first contentful paint&lt;/strong&gt; and &lt;strong&gt;lower bandwidth usage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag renders right away whatever's in its source, even if the component hasn't mounted yet, so the user will download images that might never even get to see. A slow down in the first contentful paint is also expected.&lt;/p&gt;

&lt;p&gt;The trick here is to use a placeholder, taking the original image and scaling it down to a ~10x10 pixel ratio. Is only when the &lt;em&gt;IntersectionObserver&lt;/em&gt; kicks in, that we fetch the original image. Here's a snippet of the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Proyects.js&lt;/span&gt;
&lt;span class="nf"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intersectionRatio&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;image&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;src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&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;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pro tip: instead of scaling down the images myself I used &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;, you can transform images on the fly when the &lt;em&gt;c_scale&lt;/em&gt; is provided within the url:&lt;br&gt;
 &lt;code&gt;https://res.cloudinary.com/georginagrey/image/upload/c_scale,h_12,w_12/v1532709273/portfolio/portfolio.jpg&lt;/code&gt;, if you take that bit off, you get the original image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heads up:&lt;/strong&gt; The IntersectionObserver it's is not entirely &lt;a href="https://caniuse.com/#feat=intersectionobserver" rel="noopener noreferrer"&gt;supported&lt;/a&gt; across all browsers, so you might want to use a pollyfill or a fallback.&lt;/p&gt;







&lt;h3&gt;
  
  
  The UI
&lt;/h3&gt;

&lt;p&gt;This is my weakest spot, it wasn't until recently that I kinda got my head around CSS3, or that's what I thought until I started to fall in every "gotcha" possible when styling components using just plain CSS. I had to re-write the whole thing a couple of times, until I decided to use &lt;strong&gt;&lt;a href="https://emotion.sh" rel="noopener noreferrer"&gt;emotion&lt;/a&gt;&lt;/strong&gt;, even though &lt;em&gt;css-in-js&lt;/em&gt; causes some outrage, I decided to give it a go and I loved it, I no longer have to worry about overriding rules while working on different components. &lt;/p&gt;

&lt;p&gt;The layout is quite simple, I went with a mobile-first approach, and got away with using &lt;strong&gt;flexbox&lt;/strong&gt; only.&lt;/p&gt;







&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;In a nutshell, this is a React static website hosted on a S3 bucket served by CloudFront and Route53.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/docs/create-a-new-react-app.html" rel="noopener noreferrer"&gt;Create-react-app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://emotion.sh" rel="noopener noreferrer"&gt;Emotion&lt;/a&gt; (css-in-js)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt; (for the contact form)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/es/s3/" rel="noopener noreferrer"&gt;AWS S3 bucket&lt;/a&gt; (static files hosting)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/es/cloudfront/" rel="noopener noreferrer"&gt;AWS Cloudfront&lt;/a&gt; (CDN, SSL Certificate, text compression)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/es/route53/" rel="noopener noreferrer"&gt;AWS Route53&lt;/a&gt; (DNS routing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How did I end up with that?!
&lt;/h3&gt;

&lt;p&gt;After writing the main React components and styling most of them, I stumbled with Google's &lt;a href="https://developers.google.com/web/tools/lighthouse/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; auditing tool, I downloaded the Chrome extension and generated a report (locally) and within seconds I got the results and a list of &lt;em&gt;opportunities for optimization&lt;/em&gt;, for example, by enabling &lt;em&gt;"text compression"&lt;/em&gt; in the server, the app should load about 3 seconds faster in my case. &lt;/p&gt;

&lt;p&gt;I didn't know what that meant, so after googling for a bit I came across  &lt;a href="https://aws.amazon.com/es/cloudfront/" rel="noopener noreferrer"&gt;Cloudfront&lt;/a&gt;, to top it off you can request a &lt;strong&gt;SSL certificate for free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Setting everything up is not as difficult as it may sound, &lt;a href="https://gist.github.com/bradwestfall/b5b0e450015dbc9b4e56e5f398df48ff" rel="noopener noreferrer"&gt;here&lt;/a&gt; is a very handy guide. What will you get? Hosting, increased performance, faster delivery and secure HTTPs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Is it free?
&lt;/h4&gt;

&lt;p&gt;S3 and CloudFront are not &lt;em&gt;per se&lt;/em&gt; free, is pay-as-you-go service, so for a low traffic website we would be talking about paying &lt;strong&gt;cents&lt;/strong&gt; per month if anything at all, after the 1 year free tier expires.&lt;/p&gt;

&lt;p&gt;Route53 is the DNS provider, there's a fixed price of $0.51/month per hosted zone, so we're talking only about $6/year. In this I case I already had a domain registered in Godaddy, to make it work I just grabbed the DNS names Route53 provided me with and saved them inside the &lt;em&gt;Manage Name Servers&lt;/em&gt; form in Godaddy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Caching and invalidating CloudFront
&lt;/h4&gt;

&lt;p&gt;As is expected, every time a request comes into CloudFront it will serve whatever is cached instead of going every time to your S3 bucket looking for the files, how long the content stays cached, depends on the &lt;strong&gt;default TTL&lt;/strong&gt; timeframe configured, read more about it &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since I'm still working on the site, I set the default TTL to &lt;br&gt;
3600 seconds (1 hour), I also added a header &lt;code&gt;cache-control:max-age=0&lt;/code&gt;, to the meta-data of the origin S3 bucket. But soon I'll be reverting that and use &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html" rel="noopener noreferrer"&gt;Invalidation&lt;/a&gt; instead, it force flushes the cache without needing to wait for it to expire. Doing it this way is actually cheaper too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt;&lt;br&gt;
I got my monthly statement! So, here's an example of AWS Princing with this setup:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4twrxl7mba7b4selsf4.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4twrxl7mba7b4selsf4.JPG" alt="aws billing" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CloudFront&lt;/em&gt; served +2300 requests for America/Europe tier. Plus &lt;em&gt;DNS&lt;/em&gt; routing and storage for &lt;strong&gt;$0.62&lt;/strong&gt; total. It won't get more expensive than that since a surge in traffic is not expected.&lt;/p&gt;







&lt;p&gt;That's it! ... I think 🤔&lt;/p&gt;

&lt;p&gt;This isn't my first time dealing with AWS, but it's my first coding the front-end of a website, so any comments are greatly appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for stopping by 👋&lt;/p&gt;

&lt;h6&gt;
  
  
  Edit 07/30: Added warning about IntersectionObserver.
&lt;/h6&gt;

&lt;h6&gt;
  
  
  Edit 08/03: Added AWS Billing Statement.
&lt;/h6&gt;

</description>
      <category>showdev</category>
      <category>react</category>
      <category>aws</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
