<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Alex Gurr on Medium]]></title>
        <description><![CDATA[Stories by Alex Gurr on Medium]]></description>
        <link>https://medium.com/@alexgurr?source=rss-6c4a92948081------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*uyy12g6ZwZr5wd2W4G5upw@2x.jpeg</url>
            <title>Stories by Alex Gurr on Medium</title>
            <link>https://medium.com/@alexgurr?source=rss-6c4a92948081------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 21 Jun 2026 05:50:22 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@alexgurr/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Turning a React app into an installable PWA with offline detection, service workers and theming.]]></title>
            <link>https://medium.com/@alexgurr/turning-a-react-app-into-an-installable-pwa-with-offline-detection-service-workers-and-theming-8a996b61ae31?source=rss-6c4a92948081------2</link>
            <guid isPermaLink="false">https://medium.com/p/8a996b61ae31</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[pwa]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Alex Gurr]]></dc:creator>
            <pubDate>Wed, 20 Oct 2021 09:11:23 GMT</pubDate>
            <atom:updated>2021-10-20T09:17:11.611Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*Q24c08-IfxBVUFu0Lw58og.png" /></figure><p>Recently I decided to take the dive into making my web app progressive. Some of the benefits are excellent caching, sped up page load times and the ability for a user to install it “natively”.</p><p>There are definitely some gotchas and other interesting tidbits which I’ll also be covering below.</p><p><strong>I’m using React, so I’ll assume you are too.</strong> If you want to jump in to the code, it’s all in the <a href="https://github.com/alexgurr/mixmello">mixmello GitHub repo</a>.</p><p>Let’s get started!</p><h3>Contents</h3><ul><li><a href="#0201">Setting Up Service Workers</a></li><li><a href="https://medium.com/p/8a996b61ae31#e38c">Offline Detection &amp; UI/UX</a></li><li><a href="https://medium.com/p/8a996b61ae31#f925">Icons &amp; Splash Screens</a></li><li><a href="https://medium.com/p/8a996b61ae31#06a3">Themes &amp; Theme Colours</a></li><li><a href="https://medium.com/p/8a996b61ae31#4109">Extras</a></li></ul><h3>Setting Up Service Workers</h3><p>Create-react-app provides us a couple of excellent service worker files to help us get started. They automatically configure lots of useful things like caching your webpack output. They’ll pretty much contain everything we need for our PWA.</p><p>You can get these files by running npx create-react-app my-app --template cra-template-pwa.</p><p>This will give you two files you can move into your project, serviceWorkerRegistration.js and service-worker.js. Add these into /src of your project (or use the new project provided by the command). I&#39;m not going to deep dive into these files today as they are extremely well documented via comments.</p><p>Now we actually need to register our service worker on launch. In your app index file, import the service worker.</p><pre>import { register as registerServiceWorker } from &#39;./serviceWorkerRegistration&#39;;</pre><p>Now simply run the function with registerServiceWorker();.</p><p>A finished index file should look something like this:</p><pre>import React from &#39;react&#39;;<br>import ReactDOM from &#39;react-dom&#39;;<br>import { register as registerServiceWorker } from &#39;./serviceWorkerRegistration&#39;;<br>import App from &#39;./App&#39;;</pre><pre>ReactDOM.render(<br>  &lt;React.StrictMode&gt;<br>    &lt;App /&gt;<br>  &lt;/React.StrictMode&gt;,<br>  document.getElementById(&#39;root&#39;)<br>);</pre><pre>registerServiceWorker();</pre><p>Service workers will only register/run in a production build, unless specifically enabled (see create-react-app documentation in the extras section below). This is because hot-reloading and service worker caching don’t mix very well! This means you won’t see the service worker running in Dev tools &gt; Application &gt; Service Workers.</p><h3>Offline Detection &amp; UI/UX</h3><p>Offline detection is not specifically a service worker/PWA feature, however, PWAs are ‘offline first’, meaning it’s a good idea to have code to handle offline/online state.</p><p>In my application, I decided to add a little bubble that comes down from the top of the screen and block the page. See what it looks like below (might take a few seconds to load):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VcxCPG4kptbQ1H8C.gif" /></figure><p>To make a good user &amp; developer experience for this feature -</p><ul><li>It should be a higher order component we can wrap round our whole app, for single responsibility and no code duplication</li><li>It should prevent the user from scrolling when open</li><li>It should be able to detect when the app is online/offline in real time</li><li>It should be clear what’s happening</li></ul><h4>The Component</h4><p>Let’s make a new folder, Offline. Where you put it is up to you. In my app, it&#39;s in src/common/components. I&#39;m using SCSS, but you can continue to use whatever framework your app is using.</p><p>Create 3 new files, index.js, Offline.js and _offline.scss.</p><p>index.js provides the default export for our component:</p><pre>export { default } from &#39;./Offline&#39;;</pre><p>Offline.js is our main component. The component is comprised of two main bits of functionality. 1) The window event handlers to handle network state changes and 2) the actual JSX/HTML itself. Here I&#39;m using React 17 and hooks but you could retrofit this to a class component if needed.</p><p>Let’s start building!</p><pre>export default function Offline({ children }) {<br>  return (<br>    &lt;&gt;<br>      &lt;div className=&quot;offline&quot; /&gt;<br>      {children}<br>    &lt;/&gt;<br>  );<br>}</pre><p>We’ve instantiated a new component and rendered it inside a fragment, because we don’t want to add an additional layer/container above our app’s children.</p><pre>import cx from &#39;classnames&#39;;<br>import &#39;./_offline.scss&#39;;</pre><pre>export default function Offline({ children }) {<br>  return (<br>    &lt;&gt;<br>      &lt;div className=&quot;offline&quot; /&gt;<br>      &lt;div className={cx(&#39;offline__overlay&#39;)} /&gt;<br>      {children}<br>    &lt;/&gt;<br>  );<br>}</pre><p>Now we have our styles import and an overlay div that will fade out the background. I’m using a library called classnames to chain classes but you don&#39;t have to use it. Later on, we&#39;ll conditionally change the overlay styles bases on our online/offline state.</p><pre>import cx from &#39;classnames&#39;;<br>import { ReactComponent as OfflineLogo } from &#39;assets/images/logo-offline-icon.svg&#39;;<br>import Text from &#39;../Text&#39;;<br>import &#39;./_offline.scss&#39;;</pre><pre>export default function Offline({ children }) {<br>  return (<br>    &lt;&gt;<br>      &lt;div className=&quot;offline&quot;&gt;<br>                &lt;div className=&quot;offline__content&quot;&gt;<br>                    &lt;OfflineLogo /&gt;<br>                    &lt;div className=&quot;offline__text&quot;&gt;<br>                        &lt;Text subHeading className=&quot;mt-0 mb-5&quot;&gt;You&#39;re not online&lt;/Text&gt;<br>                        &lt;Text className=&quot;mt-0 mb-0&quot;&gt;Check your internet connection.&lt;/Text&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>      &lt;div className={cx(&#39;offline__overlay&#39;)} /&gt;<br>      {children}<br>    &lt;/&gt;<br>  );<br>}</pre><p>Now we’re adding some content to our little offline bubble. Text is a component wrapper for text elements like &lt;p&gt; . I&#39;ve created a dedicated SVG logo for offline, but you can use whatever you like in it&#39;s place. The mt-x helper classes are for margin which I cover in my other article <a href="https://dev.to/alexgurr/creating-a-style-utility-file-with-some-scss-magic-5fg7">here</a>.</p><pre>import cx from &#39;classnames&#39;;<br>import { useEffect } from &#39;react&#39;;<br>import { useBooleanState, usePrevious } from &#39;webrix/hooks&#39;;<br>import { ReactComponent as OfflineLogo } from &#39;assets/images/logo-offline-icon.svg&#39;;<br>import Text from &#39;../Text&#39;;<br>import &#39;./_offline.scss&#39;;</pre><pre>export default function Offline({ children }) {<br>  const { value: online, setFalse: setOffline, setTrue: setOnline } = useBooleanState(navigator.onLine);<br>    const previousOnline = usePrevious(online);</pre><pre>    useEffect(() =&gt; {<br>        window.addEventListener(&#39;online&#39;, setOnline);<br>        window.addEventListener(&#39;offline&#39;, setOffline);</pre><pre>        return () =&gt; {<br>            window.removeEventListener(&#39;online&#39;, setOnline);<br>            window.removeEventListener(&#39;offline&#39;, setOffline);<br>        };<br>    }, []);</pre><pre>  return (<br>    &lt;&gt;<br>      &lt;div className=&quot;offline&quot;&gt;<br>                &lt;div className=&quot;offline__content&quot;&gt;<br>                    &lt;OfflineLogo /&gt;<br>                    &lt;div className=&quot;offline__text&quot;&gt;<br>                        &lt;Text subHeading className=&quot;mt-0 mb-5&quot;&gt;You&#39;re not online&lt;/Text&gt;<br>                        &lt;Text className=&quot;mt-0 mb-0&quot;&gt;Check your internet connection.&lt;/Text&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>      &lt;div className={cx(&#39;offline__overlay&#39;)} /&gt;<br>      {children}<br>    &lt;/&gt;<br>  );<br>}</pre><p>We’ve added the logic that makes it do something! We have two state variables, online which will reflect our network state (boolean) and previousOnline which allows us to prevent the overlay appearing on first load which we&#39;ll set up shortly.</p><p>The useEffect hook only runs once (on first render) and sets up our window event listeners. The function that&#39;s returned will be run on page unload and will clear those same listeners. useBooleanState is a hook provided by <a href="https://webrix.amdocs.com/docs/hooks/usebooleanstate/">webrix</a> and is a simple convenience hook for boolean manipulation.</p><pre>import cx from &#39;classnames&#39;;<br>import { useEffect } from &#39;react&#39;;<br>import { useBooleanState, usePrevious } from &#39;webrix/hooks&#39;;<br>import { ReactComponent as OfflineLogo } from &#39;assets/images/logo-offline-icon.svg&#39;;<br>import Text from &#39;../Text&#39;;<br>import &#39;./_offline.scss&#39;;</pre><pre>export default function Offline({ children }) {<br>  const { value: online, setFalse: setOffline, setTrue: setOnline } = useBooleanState(navigator.onLine);<br>    const previousOnline = usePrevious(online);</pre><pre>    useEffect(() =&gt; {<br>        window.addEventListener(&#39;online&#39;, setOnline);<br>        window.addEventListener(&#39;offline&#39;, setOffline);</pre><pre>        return () =&gt; {<br>            window.removeEventListener(&#39;online&#39;, setOnline);<br>            window.removeEventListener(&#39;offline&#39;, setOffline);<br>        };<br>    }, []);</pre><pre>  return (<br>    &lt;&gt;<br>     &lt;div<br>            className={cx(<br>                    &#39;offline&#39;,<br>                    &#39;animate__animated&#39;,<br>                    &#39;animate__faster&#39;,</pre><pre>                // This should be backticks, but the syntax highlighting gets confused so I&#39;ve made it single quotes<br>                    &#39;animate__${online ? &#39;slideOutUp&#39; : &#39;slideInDown&#39;}&#39;<br>                )}<br>                style={previousOnline === online &amp;&amp; online ? { display: &#39;none&#39; } : void 0}<br>        &gt;<br>                &lt;div className=&quot;offline__content&quot;&gt;<br>                    &lt;OfflineLogo /&gt;<br>                    &lt;div className=&quot;offline__text&quot;&gt;<br>                        &lt;Text subHeading className=&quot;mt-0 mb-5&quot;&gt;You&#39;re not online&lt;/Text&gt;<br>                        &lt;Text className=&quot;mt-0 mb-0&quot;&gt;Check your internet connection.&lt;/Text&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            &lt;div className={cx(&#39;offline__overlay&#39;, { &#39;offline__overlay--visible&#39;: !online })} /&gt;<br>      {children}<br>    &lt;/&gt;<br>  );<br>}</pre><p>Now we’ll actually use our online variable to do some cool stuff! Firstly, we&#39;re adding a conditional class to our overlay, which we&#39;ll style later.</p><p>Next, we’re making it a bit more shiny with animation! I’ve used <a href="https://animate.style/">animate.css</a> to make the bubble slide in and out of the screen. It provides us some animation classnames we can use.</p><p>Finally, we’ve added a conditional style to our container, to cover the initial load when we’re connected. This prevents the bubble from appearing and immediately sliding out of view.</p><pre>import cx from &#39;classnames&#39;;<br>import { useEffect } from &#39;react&#39;;<br>import { useBooleanState, usePrevious } from &#39;webrix/hooks&#39;;<br>import { disableBodyScroll, enableBodyScroll } from &#39;body-scroll-lock&#39;;<br>import { ReactComponent as OfflineLogo } from &#39;assets/images/logo-offline-icon.svg&#39;;<br>import Text from &#39;../Text&#39;;<br>import &#39;./_offline.scss&#39;;</pre><pre>export default function Offline({ children }) {<br>  const { value: online, setFalse: setOffline, setTrue: setOnline } = useBooleanState(navigator.onLine);<br>    const previousOnline = usePrevious(online);</pre><pre>  useEffect(() =&gt; {<br>        if (!online) { return void disableBodyScroll(document.body); }</pre><pre>        enableBodyScroll(document.body);<br>    }, [online]);</pre><pre>    useEffect(() =&gt; {<br>        window.addEventListener(&#39;online&#39;, setOnline);<br>        window.addEventListener(&#39;offline&#39;, setOffline);</pre><pre>        return () =&gt; {<br>            window.removeEventListener(&#39;online&#39;, setOnline);<br>            window.removeEventListener(&#39;offline&#39;, setOffline);<br>        };<br>    }, []);</pre><pre>  return (<br>    &lt;&gt;<br>     &lt;div<br>            className={cx(<br>                    &#39;offline&#39;,<br>                    &#39;animate__animated&#39;,<br>                    &#39;animate__faster&#39;,</pre><pre>                // This should be backticks, but the syntax highlighting gets confused so I&#39;ve made it single quotes<br>                    &#39;animate__${online ? &#39;slideOutUp&#39; : &#39;slideInDown&#39;}&#39;<br>                )}<br>                style={previousOnline === online &amp;&amp; online ? { display: &#39;none&#39; } : void 0}<br>        &gt;<br>                &lt;div className=&quot;offline__content&quot;&gt;<br>                    &lt;OfflineLogo /&gt;<br>                    &lt;div className=&quot;offline__text&quot;&gt;<br>                        &lt;Text subHeading className=&quot;mt-0 mb-5&quot;&gt;You&#39;re not online&lt;/Text&gt;<br>                        &lt;Text className=&quot;mt-0 mb-0&quot;&gt;Check your internet connection.&lt;/Text&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            &lt;div className={cx(&#39;offline__overlay&#39;, { &#39;offline__overlay--visible&#39;: !online })} /&gt;<br>      {children}<br>    &lt;/&gt;<br>  );<br>}</pre><p>Last but not least, let’s lock scrolling. Remember the earlier requirement? When the overlay and bubble are open the user shouldn’t be able to scroll in the background. For this, we use a library called body-scroll-lock and simply toggle the lock in our new useEffect hook.</p><h4>The Styling</h4><p>Styling in SCSS is pretty simple. Here’s how we can get the result above:</p><pre>@import &#39;vars&#39;;</pre><pre>.offline {<br>  position: fixed;<br>  top: 0;<br>  z-index: 4;<br>  left: calc(50% - 200px);<br>  width: 400px;<br>  padding-top: 40px;</pre><pre>  @media only screen and (max-width: $mobile-width) {<br>    padding-top: 20px;<br>  }</pre><pre>  @media only screen and (max-width: 500px) {<br>    padding-top: 20px;<br>    width: calc(100% - 40px);<br>    left: 20px;<br>  }</pre><pre>  &amp;__content {<br>    padding: 15px 20px;<br>    background: white;<br>    display: flex;<br>    align-items: center;<br>    justify-content: center;<br>    border-radius: 6px;</pre><pre>    &gt; svg {<br>      height: 50px;<br>      width: auto;<br>      margin-right: 20px;<br>    }<br>  }</pre><pre>  &amp;__overlay {<br>    position: fixed;<br>    z-index: 3;<br>    background: rgba(0, 0, 0, 0.8);<br>    top: 0;<br>    left: 0;<br>    width: 100vw;<br>    height: 100vh;<br>    opacity: 0;<br>    transition: opacity 0.5s ease-in-out;<br>    pointer-events: none;</pre><pre>    &amp;--visible {<br>      opacity: 1;<br>      pointer-events: unset;<br>    }<br>  }<br>}</pre><p>Parts worth talking about are:</p><ul><li>Hardcoded right %, instead of translate. animate.css uses transforms to animate, so we need a different approach to center it horizontally.</li><li>@import &#39;vars&#39; - this is just a file full of SCSS variables. The media query variable is just a pixel value.</li><li>padding: top instead of an actual top value - animate.css uses transform: translateY(-100%) on the container when sliding it out. If we use a top value, the component won&#39;t slide completely out of view. If we give it padding instead, we are making the component larger and therefore will all slide out, but still have the gap from the top of the screen.</li></ul><h4>Using It In Our App</h4><p>You can use the component wherever you want, but I recommend as high as possible. In mine, it’s in the app index file:</p><pre>ReactDOM.render(<br>  &lt;React.StrictMode&gt;<br>    &lt;Offline&gt;<br>        &lt;App /&gt;<br>    &lt;/Offline&gt;<br>  &lt;/React.StrictMode&gt;,<br>  document.getElementById(&#39;root&#39;)<br>);</pre><h3>Icons &amp; Splash Screens</h3><h4>Manifest.json</h4><p>The manifest file is used to tell platforms how we want our PWA to behave. create-react-app creates a manifest.json file automatically for us, in the public folder.</p><pre>{<br>  &quot;short_name&quot;: &quot;name&quot;,<br>  &quot;name&quot;: &quot;name&quot;,<br>  &quot;description&quot;: &quot;description&quot;,<br>  &quot;icons&quot;: [<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-72x72.png&quot;,<br>      &quot;sizes&quot;: &quot;72x72&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-96x96.png&quot;,<br>      &quot;sizes&quot;: &quot;96x96&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-128x128.png&quot;,<br>      &quot;sizes&quot;: &quot;128x128&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-144x144.png&quot;,<br>      &quot;sizes&quot;: &quot;144x144&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-152x152.png&quot;,<br>      &quot;sizes&quot;: &quot;152x152&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-192x192.png&quot;,<br>      &quot;sizes&quot;: &quot;192x192&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-384x384.png&quot;,<br>      &quot;sizes&quot;: &quot;384x384&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    },<br>    {<br>      &quot;src&quot;: &quot;/icons/icon-512x512.png&quot;,<br>      &quot;sizes&quot;: &quot;512x512&quot;,<br>      &quot;type&quot;: &quot;image/png&quot;<br>    }<br>  ],<br>  &quot;start_url&quot;: &quot;.&quot;,<br>  &quot;display&quot;: &quot;standalone&quot;,<br>  &quot;background_color&quot;: &quot;#fff&quot;<br>}</pre><p>short_name - the title that&#39;s displayed on smaller areas, such as on home screens</p><p>name - the full title of the app</p><p>description - app description</p><p>icons - these are icons used on an android home screen or for PWA desktop apps on desktop.<strong>These are not used on iOS PWAs</strong> (see gotchas below)</p><p>start_url - entry point to your application. For standard React apps, this will be root, or .</p><p>display - how should your app be displayed within a PWA container? standalone will render full screen and give a more <em>native</em> experience</p><p>background_color - loading screen background colour (such as on a splash screen). This is not the background colour of your app when loaded.</p><p>theme_color - this dictates the color of the status bar at the top of the app, however I choose to just use the theme &lt;meta&gt; tag in index.html as I can dynamically change it (see themes below).</p><p>For my app, I took my app’s logo and turned it into a macOS-esque rounded icon, such as:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*2bmvEywaTdzL9JTK.jpg" /></figure><p>Full breakdown of the manifest.json file can be found <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json">here</a>. Your index.html file should link to this manifest, with a line similar to &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot; /&gt;.</p><h4>iOS &amp; Gotchas</h4><p>iOS <strong>still</strong> doesn’t handle PWAs very well. Your manifest file will be pretty much ignored, other than to tell iOS you support PWAs. PWAs are only supported via <strong>Safari</strong>.</p><p>iOS <strong>does not support transparency</strong> on icons. It’ll render a black background behind your icon if it’s a png. You should make special icons for iOS, with a coloured background (mine’s white), which looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/518/0*Q9WxP7kfFrHSc1nS.png" /></figure><p>To use it, we’ll need the link &lt;link rel=&quot;apple-touch-icon&quot; href=&quot;%PUBLIC_URL%/icons/ios-touch-icon.png&quot;&gt; in our index.html file.</p><h4>Splash Screens</h4><p>To show a splash screen on iOS when the app’s loading, you’ll need a series of html code lines in index.html. Unfortunately, you&#39;ll need a different sized image per supported resolution:</p><pre>&lt;link href=&quot;%PUBLIC_URL%/splash/iphone5_splash.png&quot; media=&quot;(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/iphone6_splash.png&quot; media=&quot;(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/iphoneplus_splash.png&quot; media=&quot;(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/iphonex_splash.png&quot; media=&quot;(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/iphonexr_splash.png&quot; media=&quot;(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/iphonexsmax_splash.png&quot; media=&quot;(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/ipad_splash.png&quot; media=&quot;(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/ipadpro1_splash.png&quot; media=&quot;(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/ipadpro3_splash.png&quot; media=&quot;(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;<br>&lt;link href=&quot;%PUBLIC_URL%/splash/ipadpro2_splash.png&quot; media=&quot;(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)&quot; rel=&quot;apple-touch-startup-image&quot; /&gt;</pre><h3>Themes &amp; Theme Colours</h3><p>As mentioned before, we’ll control theme through index.html and not using manifest.json. Find out more about theme-color and what it looks like in action, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color">here</a>.</p><h4>Static Theme Colour</h4><p>Static theme colours are easy. Simply include this line in your index.html file. &lt;meta name=&quot;theme-color&quot; content=&quot;#ffffff&quot; /&gt; . create-react-app provides this by default.</p><h4>Dynamic Theme Colour</h4><p>In your app, you might have different page colours. For example, in my app, the homepage is green, but the rest are white. I wanted the theme-color to change based on where I was. When a Modal window opens, the theme-color becomes black.</p><p>For this, you’ll need a library called react-helmet. Helmet allows us to modify the &lt;head&gt; of our document from within our components. Sweet!</p><p>To do this, simply include the &lt;Helmet&gt; element in any of your components:</p><pre>&lt;Helmet&gt;&lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot; /&gt;&lt;/Helmet&gt;</pre><p>We can actually extend the Offline.js component we built earlier to make the status bar black:</p><pre>&lt;div<br>    className={cx(<br>        &#39;offline&#39;,<br>        &#39;animate__animated&#39;,<br>        &#39;animate__faster&#39;,</pre><pre>    // This should be backticks, but the syntax highlighting gets confused so I&#39;ve made it single quotes<br>        &#39;animate__${online ? &#39;slideOutUp&#39; : &#39;slideInDown&#39;}&#39;<br>    )}<br>    style={previousOnline === online &amp;&amp; online ? { display: &#39;none&#39; } : void 0}<br>&gt;</pre><pre>  // The line below changes the theme dynamically, but only when we&#39;re offline<br>    {!online &amp;&amp; &lt;Helmet&gt;&lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot; /&gt;&lt;/Helmet&gt;}</pre><pre>    &lt;div className=&quot;offline__content&quot;&gt;<br>        &lt;OfflineLogo /&gt;<br>        &lt;div className=&quot;offline__text&quot;&gt;<br>            &lt;Text subHeading className=&quot;mt-0 mb-5&quot;&gt;You&#39;re not online&lt;/Text&gt;<br>            &lt;Text className=&quot;mt-0 mb-0&quot;&gt;Check your internet connection.&lt;/Text&gt;<br>        &lt;/div&gt;<br>    &lt;/div&gt;<br>&lt;/div&gt;</pre><h3>Extras</h3><h4>Links</h4><ul><li><a href="https://developers.google.com/web/ilt/pwa">Google PWA deep dive</a></li><li><a href="https://create-react-app.dev/docs/making-a-progressive-web-app/">Create-react-app PWA documentation</a></li><li><a href="https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html">Apple PWA documentation</a></li></ul><h3>Thanks for reading! Feel free to leave feedback 🚀</h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8a996b61ae31" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What is function Memoization and why should you care?]]></title>
            <link>https://medium.com/@alexgurr/what-is-function-memoization-and-why-should-you-care-f95bcae4e80f?source=rss-6c4a92948081------2</link>
            <guid isPermaLink="false">https://medium.com/p/f95bcae4e80f</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[memoization]]></category>
            <dc:creator><![CDATA[Alex Gurr]]></dc:creator>
            <pubDate>Wed, 20 Oct 2021 08:25:52 GMT</pubDate>
            <atom:updated>2021-10-20T08:25:52.007Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="memo-promo" src="https://cdn-images-1.medium.com/max/1024/1*e3YTMWjfVk3ctCPRtPomYQ.png" /></figure><p>Memoization is a general software engineering principal/idealogy that can be applied to code in any language. My examples and libraries will all be JavaScript.</p><h3>So What is Memoization?</h3><p>Memoization is the principal of caching the result of a function call. If you call a function multiple times with the same arguments, you’ll get the cached result every time. The logic in your function will not re-run when there’s a cached result.</p><h3>Why/When Would I Ever Need This?</h3><p>Memoization is great when you find functions being called over and over again (such as in a render call in React). Your function might have some complex logic that your performance would benefit from by not calling the same logic over and over.</p><p><em>tl;dr performance for functions called multiple times with the same arguments.</em></p><h3>Memoization in React</h3><p>The concept of Memoization in React is exactly the same. We want to cache the result of a function call. Except in this scenario, our function returns JSX and our arguments are props.</p><p>If you have a parent being re-rendered, your child function will be called on every render, even if the props don’t change. React provides us a React.memo utility and a useMemo hook which we can utilise in our functional components to prevent unnecessary re-renders.</p><p>We can also utilise normal Memoization in class methods and other JS functions in our react components. A traditional pattern in React class components was to react to prop changes through componentWillReceiveProps, apply some logic to a prop and set it in state. Now that componentWillReceiveProps is on the way to being deprecated, Memoization provides us a great alternative method to achieving the same result. See the examples section below.</p><p><a href="https://reactjs.org/docs/react-api.html#reactmemo">https://reactjs.org/docs/react-api.html#reactmemo</a></p><h3>Some vanilla JS memoization Libraries</h3><p>For general JavaScript, I’d recommend two battle-tested libraries instead of trying to implement yourself, which I’ve covered below.</p><h3>Lodash.memoize</h3><p>Creates a memoization results map, meaning it will effectively store the history of all results for use in the future.</p><p>Serialises only the first argument to string. Be careful about passing objects. Multiple arguments aren’t compared.</p><p>Useful if you’re calling the function from multiple places with different arguments.</p><p><a href="https://lodash.com/docs/4.17.15#memoize">https://lodash.com/docs/4.17.15#memoize</a></p><h3>Memoize One</h3><p>Stores the last result of the function call. Will only ever compare the arguments to the last ones the function was called with.</p><p>Uses all the arguments for comparison between function calls. No serialisation of objects so you can pass anything.</p><p>Useful if you’re only calling the memoized function from one place.</p><p><a href="https://github.com/alexreardon/memoize-one">https://github.com/alexreardon/memoize-one</a></p><h3>Differences Between the Two</h3><ul><li>Lodash memoize will <em>serialize</em> the arguments to use as a map key</li><li>Lodash memoize will only use the <em>first</em> argument</li><li>Memoize One will only remember the set of arguments/result of the <em>previous</em> function call. Lodash memoize will maintain a result map.</li></ul><h3>How About Some Examples?</h3><h3>A normal function</h3><pre>import _memoize from &#39;lodash.memoize&#39;;<br>import memoizeOne from &#39;memoize-one&#39;;</pre><pre>const myFunc = users =&gt; users.filter(user =&gt; user.gender === &#39;female&#39;);</pre><pre>const myMemoizedFunc = _memoize(user =&gt; users.filter(user =&gt; user.gender === &#39;female&#39;));</pre><pre>const myMemoizedOnceFunc = memoizeOne(user =&gt; users.filter(user =&gt; user.gender === &#39;female&#39;));</pre><h3>React.memo</h3><pre>import React, { memo } from &#39;react&#39;;</pre><pre>function MyFunctionalComponent {<br>  return &lt;div /&gt;;<br>}</pre><pre>export default memo(MyFunctionalComponent);</pre><h3>Before/After, React class component real world scenario</h3><h4>Before</h4><pre>import React, { Component } from &#39;react&#39;;</pre><pre>function filterUsers(users) {<br>  return users.filter(({ gender }) =&gt; gender === &#39;female&#39;);<br>}</pre><pre>export default class FemaleUserList extends Component {<br>  constructor(props) {<br>    super(props);</pre><pre>    const { allUsers } = props;</pre><pre>    this.state = {<br>      femaleUsers: filterUsers(allUsers)<br>    }<br>  }</pre><pre>  componentWillReceiveProps(nextProps) {<br>    const { allUsers } = nextProps;</pre><pre>    if (allUsers !== this.props.allUsers) {<br>      this.setState({<br>        femaleUsers: filterUsers(allUsers)<br>      });<br>    }<br>  }</pre><pre>  render() {<br>    const { femaleUsers } = this.state;</pre><pre>    return femaleUsers.map(User);<br>  }  <br>}</pre><h4>After</h4><pre>import React, { Component } from &#39;react&#39;;<br>import memoizeOne from &#39;memoize-one&#39;;</pre><pre>export default class FemaleUserList extends Component {<br>  // We bind this function to the class now because the cached results are scoped to this class instance<br>  filterUsers = memoizeOne(users =&gt; users.filter(({ gender }) =&gt; gender === &#39;female&#39;));</pre><pre>  render() {<br>    const { allUsers  } = this.props;<br>    const femaleUsers = this.filterUsers(allUsers);</pre><pre>    return femaleUsers.map(User);<br>  }<br>}</pre><h3>A React form</h3><pre>import React, { Component } from &#39;react&#39;;<br>import _memoize from &#39;lodash.memoize&#39;;</pre><pre>export default class FemaleUserList extends Component {<br>  // Yes, we can even return cached functions! This means we don&#39;t have to<br>  // keep creating new anonymous functions<br>  handleFieldChange = _memoize((fieldName) =&gt; ({ target: { value } }) =&gt; {<br>    this.setState({ [fieldName]: value });<br>  }); <br></pre><pre>  render() {<br>    const { email, password } = this.state;</pre><pre>    return (<br>      &lt;div&gt;<br>        &lt;input onChange={this.handleFieldChange(&#39;email&#39;)} value={email} /&gt;<br>        &lt;input<br>          onChange={this.handleFieldChange(&#39;password&#39;)}<br>          value={password}<br>          type=&quot;password&quot;<br>        /&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}</pre><h3>Closing Words</h3><p>Memoization is a great tool in a developer’s arsenal. When used correctly and in the right places, it can provide great performance improvements.</p><p>Just be aware of the gotchas, especially when using <em>React.memo</em> and expecting things to re-render.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f95bcae4e80f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating a style utility file with some SCSS magic ✨]]></title>
            <link>https://medium.com/@alexgurr/creating-a-style-utility-file-with-some-scss-magic-225f4de799f4?source=rss-6c4a92948081------2</link>
            <guid isPermaLink="false">https://medium.com/p/225f4de799f4</guid>
            <category><![CDATA[stylesheets]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[scss]]></category>
            <dc:creator><![CDATA[Alex Gurr]]></dc:creator>
            <pubDate>Wed, 20 Oct 2021 08:02:01 GMT</pubDate>
            <atom:updated>2021-10-20T08:02:01.046Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="sass-promo" src="https://cdn-images-1.medium.com/max/1024/1*KSxuJeYgo1AS0C2TjnvtnQ.png" /></figure><p>You might have used style frameworks in the past like <em>Bootstrap</em> or <em>Bulma</em>. Most of these types of libraries provide <strong>utility</strong> classes you can easily use in your HTML layouts, such as ml-10 for left margin, or pr-5 for right padding. One way of doing this is with a super long file and repetitive class declarations. Not very readable or scalable though is it?</p><p>In this article, we’ll dive into <strong>making our own utility style file with scss</strong>, using some cool scss magic you might not have used before. In our example we’ll be working with margin, but you could easily reproduce with any properties you like! The way we do this is flexible -- you provide the values for your classes (instead of mapping 1-100 for example).</p><h3>Let’s make a new .scss file</h3><p>Pretty easy. I’ll let you decide on how you want to do this.</p><p><em>Tip: you can structure your scss files similarly to JS files and make it super readable! I love using index.scss files to group imports within a folder, then having the parent import the index file.</em></p><h3>Let’s set up some variables</h3><p>We’re going to need 4 variables (a couple are optional):</p><p>auto (optional) - good practice to extract constants to variables. This is used to hold the auto value.</p><p>directions - an array of strings, to cover the 4 different directions our margin could be (top, bottom, left, right)</p><p>css-property-map (optional) - A scss map to map our directions above to CSS properties. SCSS maps work similarly to JS, with a key/value. <em>I use this because I want my utility classes to be shorthand/small in size. You could avoid this by having the full direction in your class.</em></p><p>sizes: an array of CSS values. These are the utility classes/values we want to generate. I&#39;ve also included an auto value which gets handled separately as it&#39;s not a standard px value.</p><pre>$auto: auto;<br>$directions: &#39;t&#39;, &#39;b&#39;, &#39;l&#39;, &#39;r&#39;;<br>$css-property-map: (&#39;t&#39;: margin-top, &#39;b&#39;: margin-bottom, &#39;l&#39;: margin-left, &#39;r&#39;: margin-right);<br>$sizes: 0, 5, 10, 20, 30, 40, 50, 60, 100, $auto;</pre><h3>And now some loops</h3><p>We set up a couple of dynamic loops (think for each loop in JS). This will give us all sizes for each direction.</p><pre>@each $direction in $directions {<br>  @each $size in $sizes {<br>    // Classes coming here<br>  }<br>}</pre><h3>Generate the actual utility class</h3><p>We dynamically create CSS classes for each direction/size combo. This will give us the classes in a .ml-5 format.</p><pre>@each $direction in $directions {<br>  @each $size in $sizes {<br>    .m#{$direction}-#{$size} {<br>      // CSS property/values coming here<br>    }<br>  }<br>}</pre><h3>Setup the CSS properties and values</h3><p>We compare the size to our previously defined auto value. If it&#39;s auto, we don&#39;t need to build a px value.</p><pre>@if $size == $auto {<br>  #{map-get($css-property-map, $direction)}: $auto;<br>} @else {<br>  #{map-get($css-property-map, $direction)}: #{$size}px;<br>}</pre><p>#{map-get($css-property-map, $direction)} let&#39;s us build dynamic CSS properties. Let&#39;s deep dive into what each bit means:</p><ul><li>#{} - inject an SCSS variable into CSS</li><li>map-get(map, key) - pull out a value from a specific map with the passed key</li></ul><h3>Complete util code</h3><pre>$auto: auto;<br>$directions: &#39;t&#39;, &#39;b&#39;, &#39;l&#39;, &#39;r&#39;;<br>$css-property-map: (&#39;t&#39;: margin-top, &#39;b&#39;: margin-bottom, &#39;l&#39;: margin-left, &#39;r&#39;: margin-right);<br>$sizes: 0, 5, 10, 20, 30, 40, 50, 60, 100, $auto;</pre><pre>@each $direction in $directions {<br>  @each $size in $sizes {<br>    .m#{$direction}-#{$size} {<br>      @if $size == $auto {<br>        #{map-get($css-property-map, $direction)}: $auto;<br>      } @else {<br>        #{map-get($css-property-map, $direction)}: #{$size}px;<br>      }<br>    }<br>  }<br>}</pre><h3>And that’s it! We’ve now dynamically generated a collection of top/bottom/left/right margin classes.</h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=225f4de799f4" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>