<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ progressive web app - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ progressive web app - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:30:54 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/progressive-web-app/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ What are Progressive Web Apps? PWA Guide for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ Progressive Web Apps (PWAs) are simply installable web applications – websites that you can install on your device, much like you would install an app from an app store.  They bring together the best parts of using a website (easy to access, no need ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-are-progressive-web-apps-pwa-guide/</link>
                <guid isPermaLink="false">66c5a34c3d77fae9eb82a477</guid>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ophy Boamah ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jan 2024 15:56:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/Essentials.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Progressive Web Apps (PWAs) are simply installable web applications – websites that you can install on your device, much like you would install an app from an app store. </p>
<p>They bring together the best parts of using a website (easy to access, no need to install anything) with the great features of mobile apps (fast, can work offline), offering a high-quality user experience. </p>
<p>The core of a PWA's usefulness lies in the offline-first approach, where applications are designed to function seamlessly without a constant internet connection. This means you can still use these apps even when your internet is slow or not available, and makes PWAs very user-friendly and accessible, even where an internet connection is not always reliable.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/pwaa.png" alt="Image" width="600" height="400" loading="lazy">
<em>Graphic showing a native app vs a web application vs a PWA</em></p>
<h2 id="heading-5-reasons-to-use-a-pwa-instead-of-a-native-app">5 Reasons to Use a PWA Instead of a Native app</h2>
<p>Progressive Web Apps (PWAs) are reshaping the landscape of digital interaction. They offer a hybrid experience that combines the best features of web and mobile apps. </p>
<p>Let's delve deeper into why PWAs are often a superior choice compared to native apps:</p>
<h3 id="heading-1-speed-and-ease-of-installation">1. Speed and Ease of Installation</h3>
<p>PWAs eliminate the lengthy download and installation process typical of native apps. You can access a PWA as swiftly as you'd load a web page, making it as straightforward as bookmarking your favorite site – no waiting for downloads or navigating through app stores. </p>
<p>Consider Twitter Lite, a PWA version of the popular social media platform, which offers the full Twitter experience without the need to wait for a separate app download. </p>
<h3 id="heading-2-minimal-storage-requirements">2. Minimal Storage Requirements</h3>
<p>One of the most significant advantages of PWAs is their minimal storage requirements. Since they primarily store data online, much like cloud services, they occupy significantly less space on a user’s device. </p>
<p>This feature is particularly beneficial for users with limited device storage or expensive data plans. For example, Pinterest’s PWA takes up less space than its native counterpart but still provides a rich, engaging user experience. </p>
<h3 id="heading-3-reliable-offline-performance">3. Reliable Offline Performance</h3>
<p>A PWA's ability to function offline is like having a good book downloaded to your e-reader, ready to enjoy even when you're out of range of a good signal. </p>
<p>PWAs can function effectively even in areas with poor or no internet connectivity, using cached data from previous online activities. This ensures that users have uninterrupted access to essential features. </p>
<p>A notable example is the Starbucks PWA, which allows customers to browse the menu and customize orders, regardless of their internet connection for a consistent user experience.</p>
<h3 id="heading-4-cost-effective-development-and-cross-device-compatibility">4. Cost-Effective Development and Cross-Device Compatibility</h3>
<p>Developing a PWA can be more cost-effective than building native apps for various platforms. This unified approach not only saves significant development time and resources but also simplifies maintenance and updates. </p>
<p>Businesses like Uber have leveraged PWAs to provide a seamless user experience across all devices without the need for multiple native apps. </p>
<h3 id="heading-5-automatic-updates-and-push-notifications">5. Automatic Updates and Push Notifications</h3>
<p>PWAs update themselves seamlessly, much like a web page loading the latest content each time it’s visited. Users always have access to the most current version of the app without going through the process of manual updates. This feature ensures that users are not hindered by outdated versions, a common issue with native apps. </p>
<p>For instance, Google Maps’ PWA automatically incorporates the latest features and improvements without user intervention. In addition to staying current, PWAs can engage users with push notifications, just like native apps, keeping them informed and involved without the need for a visit to the app store. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/pwass.png" alt="Image" width="600" height="400" loading="lazy">
<em>Infographic of a Progressive Web App (PWA) showcasing its features: adaptability across devices, security, offline functionality, fast performance, updates, and push notifications.</em></p>
<h2 id="heading-pwa-core-concepts">PWA Core Concepts</h2>
<p>To fully grasp the potential and functionality of Progressive Web Apps, it's essential to understand their foundational components. These core concepts not only define the structure and behavior of PWAs but also distinguish them from traditional web applications.</p>
<h3 id="heading-service-workers-the-backbone-of-pwas">Service Workers: The Backbone of PWAs</h3>
<p>Service workers are scripts that run in the background, separate from your web page. They act as a proxy between the web application and the network. </p>
<p>Think of service workers as behind-the-scenes helpers for your web app. Their main job is to manage how your app talks to the internet. </p>
<p>They can save important parts of your app on the user's device, which means your app can work even when there's no internet. They're also in charge of quietly updating app content and can send notifications, just like a native app on your phone.</p>
<h3 id="heading-the-app-manifest-your-pwas-identity">The App Manifest: Your PWA's Identity</h3>
<p>The web app manifest is a JSON file which is essential because it tells the user's device how your app should look and behave. </p>
<p>It's like your app's ID card – it includes the app's name, the icons it uses, the first page it should open, and how it should display (like in full screen). This file makes your web app feel more like a regular app, allowing users to 'install' it on their home screen.</p>
<h3 id="heading-caching-the-key-to-offline-functionality">Caching: The Key to Offline Functionality</h3>
<p>Effective caching is vital for a robust offline experience. Caching is like your app's memory. It stores important parts of your app so they can be quickly loaded later, even if there's no internet. It's crucial for making your app work offline. </p>
<p>There are different ways to handle caching, such as cache-first (where the app checks the cache before the internet), network-first (the opposite), and stale-while-revalidate (a mix of both). The choice depends on what your app does and what kind of information it handles, affecting how your app stores and retrieves its data.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/pwa-components.png" alt="Image" width="600" height="400" loading="lazy">
<em>Icons representing key components of a PWA: Service worker, Manifest and HTTPS</em></p>
<h2 id="heading-overview-of-building-a-pwa">Overview of Building a PWA</h2>
<p>Building a Progressive Web App (PWA) might sound complex, but it's quite manageable when you break it down into steps. In this article, I'll give you an overview of the process – and in my next tutorial, I'll go into the process in more detail.</p>
<h3 id="heading-1-start-with-a-basic-webpage">1. Start with a Basic Webpage:</h3>
<p>Create a simple website using HTML for structure, CSS for style, and JavaScript for functionality.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Ophy's To-Do List PWA<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Ophy's To-Do List<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"todo-input"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Add a new task..."</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"addTask()"</span>&gt;</span>Add<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"todo-list"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Tasks will go here --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Arial'</span>, sans-serif;
}

<span class="hljs-selector-id">#todo-list</span> {
    <span class="hljs-attribute">list-style-type</span>: none;
}

<span class="hljs-selector-id">#todo-list</span> <span class="hljs-selector-tag">li</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f2f2f2</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">3px</span>;
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTask</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> input = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'todo-input'</span>);
    <span class="hljs-keyword">var</span> newTask = input.value;
    <span class="hljs-keyword">if</span> (newTask) {
        <span class="hljs-keyword">var</span> listItem = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'li'</span>);
        listItem.textContent = newTask;
        <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'todo-list'</span>).appendChild(listItem);
        input.value = <span class="hljs-string">''</span>; <span class="hljs-comment">// Clear the input</span>
    }
}
</code></pre>
<h3 id="heading-2-create-a-manifest-file">2. Create a Manifest File:</h3>
<p>In the manifest file, write down your app's name, the icons it uses, and the first page it should open. This makes your website act more like an app you can install.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Ophy's To-Do List"</span>,
    <span class="hljs-attr">"short_name"</span>: <span class="hljs-string">"OphyToDo"</span>,
    <span class="hljs-attr">"icons"</span>: [
        {
            <span class="hljs-attr">"src"</span>: <span class="hljs-string">"favicon.ico"</span>,
            <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"64x64 32x32 24x24 16x16"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/x-icon"</span>
        }
    ],
    <span class="hljs-attr">"start_url"</span>: <span class="hljs-string">"/"</span>,
    <span class="hljs-attr">"display"</span>: <span class="hljs-string">"standalone"</span>,
    <span class="hljs-attr">"theme_color"</span>: <span class="hljs-string">"#000000"</span>,
    <span class="hljs-attr">"background_color"</span>: <span class="hljs-string">"#ffffff"</span>
}
</code></pre>
<h3 id="heading-3-set-up-a-service-worker">3. Set Up a Service Worker:</h3>
<p>In your main JavaScript file, add a service worker. This is a special script that works separately from your website.</p>
<p>The service worker's job is to handle how your app stores and retrieves data, especially for offline use.</p>
<pre><code class="lang-javascript">self.addEventListener(<span class="hljs-string">'install'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-comment">// Perform install steps</span>
    <span class="hljs-keyword">var</span> CACHE_NAME = <span class="hljs-string">'ophy-todo-cache-v1'</span>;
    <span class="hljs-keyword">var</span> urlsToCache = [
        <span class="hljs-string">'/'</span>,
        <span class="hljs-string">'/styles.css'</span>,
        <span class="hljs-string">'/app.js'</span>
    ];

    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">cache</span>) </span>{
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Opened cache'</span>);
                <span class="hljs-keyword">return</span> cache.addAll(urlsToCache);
            })
    );
});
</code></pre>
<p>Following these steps will help you build a simple PWA that users can install and use, even when they're offline.</p>
<h2 id="heading-real-world-use-cases-for-pwas">Real World Use Cases for PWAs</h2>
<p>These real-world examples demonstrate the practical benefits and impact of offline-first strategies:</p>
<ol>
<li><strong>Starbucks</strong>: Starbucks has a PWA that lets customers look at the menu and order drinks and food even when they're offline. This not only makes the experience better for customers but also helps increase sales and customer interaction with the brand, as they can order anytime, anywhere.</li>
<li><strong>Khan academy</strong>: Khan Academy, a well-known educational site, has a PWA allowing students to download and access lessons and courses without an internet connection. This feature is particularly helpful for those in areas with unreliable internet, ensuring uninterrupted learning. Just like the Starbucks PWA, Khan Academy's use of this technology improves the user experience, making education more accessible and flexible.</li>
<li><strong>Trivago</strong>: Trivago, a popular hotel search engine, developed a PWA to enhance user experience, especially for travelers with unstable internet connections. The PWA allows users to browse hotel deals and information offline, making trip planning more flexible and accessible. This has led to increased user engagement and higher conversion rates, as travelers can continue interacting with the app even in areas with poor connectivity.</li>
<li><strong>Forbes</strong>: Forbes, a leading global media company, launched a PWA to improve the mobile experience for its readers. The PWA significantly reduced loading times and allowed offline reading of articles. This innovation not only enhanced user engagement but also resulted in a noticeable increase in readership and time spent on the site. The Forbes PWA demonstrates how media outlets can leverage offline-first strategies to reach and retain a wider audience, regardless of their internet connectivity.</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>PWAs are changing the game in web development, bringing features we usually see in native apps to the web. </p>
<p>They offer a blend of the accessibility of web applications with the engaging user experience of native apps. They represent a forward-thinking solution for businesses and developers looking to maximize reach and functionality while minimizing costs and complexity. </p>
<p>PWAs aren't just a passing trend – they're the next big thing in how we make and use web apps. By focusing on working offline, PWAs provide a more reliable and user-friendly experience. </p>
<p>Whether you're just curious or ready to start building, the following resources remain great places to begin exploring the possibilities of offline-first web applications.</p>
<ul>
<li><a target="_blank" href="https://developers.google.com/web/progressive-web-apps/checklist">Google's PWA Checklist</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">MDN Web Docs on Service Workers</a></li>
<li><a target="_blank" href="https://web.dev/progressive-web-apps/">Web.dev for PWA tutorials</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is a PWA? Progressive Web Apps for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ These days, everything is made possible with the help of mobile phones and applications. Let's say you need to order food – you can do so instantly via the company's app. Maybe you need government services – the same thing applies. You can even get m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-are-progressive-web-apps/</link>
                <guid isPermaLink="false">66b8dc0a34a5a4a0df79b3ee</guid>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                    <category>
                        <![CDATA[ responsive design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hillary Nyakundi ]]>
                </dc:creator>
                <pubDate>Tue, 27 Jun 2023 12:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/CoverImagePWA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>These days, everything is made possible with the help of mobile phones and applications.</p>
<p>Let's say you need to order food – you can do so instantly via the company's app. Maybe you need government services – the same thing applies. You can even get medical emergency dial services via an app.</p>
<p>There's an app for everything – from banking to studying and from trading to shopping. Every business has an app, and even our governments have simplified their services into app form.</p>
<p>Hold on, building and maintaining an app is cumbersome, and it's quite expensive for small businesses, so how do they manage?</p>
<p>Well it's simple: with the help of advancements in technology there is an option that helps small businesses out. This option combines the features of an app with the technology used in web development to build affordable services for businesses – I'm talking about <strong>Progressive Web Apps</strong>.</p>
<p>Let's dive in and get a better understanding of what PWAs are all about.</p>
<h2 id="heading-what-is-a-progressive-web-app">What is a Progressive Web App?</h2>
<p>Progressive Web Applications (PWAs) are apps built with web technologies that we probably all know and love, like HTML, CSS, and JavaScript. But they have the feel and functionality of an actual native app. Wait a minute! Native Apps, what do we mean by this?</p>
<p>A <strong>Native App</strong> is a software application built in a specific programming language for a specific device platform, either IOS or Android.<br>PWAs are built with the capabilities like push notifications and the ability to work offline. They are also built on and enhanced with modern APIs which makes it easy to deliver improved capabilities along with reliability and the ability to install them on any device.</p>
<p>PWAs takes advantage of the huge web ecosystem this is inclusive of the plugins, and community and the relative ease of deploying and keeping a website contrary to a native application which is pretty difficult to develop. This means you can build a PWA quickly and easily.</p>
<p>With its popularity many companies have shifted into the product, I tend to believe that this is because of its ability to run on an android and iOS without much difference. Some good examples of top companies who have their products as PWAs include: <em>Twitter, Pintrest, Uber, Tiktok, Spotify, Jumia (a leading e-commerce site in Africa)</em> etc...</p>
<p>A common feature about this products is that they are all installable on your home screen, able to work offline from where you last left and offer a comparable experience and features to their native apps.</p>
<p>Just like when building a native mobile app there are some expectations that should be met to make a good product for consumer use, the same thing applies to PWAs. Let's discuss what makes a good PWA.</p>
<h2 id="heading-characteristics-of-pwas">Characteristics of PWAs</h2>
<p>Below is what should be considered when developing a PWA:</p>
<h3 id="heading-responsiveness">Responsiveness</h3>
<p>Different companies produce gadgets with different screen sizes, and as a developer it's your responsibility to ensure all the different users enjoy the product regardless the device they are using. So it's a good idea to make sure your app can be used on any screen size and it's content is available at any view-port size.</p>
<h3 id="heading-installable">Installable</h3>
<p>Research has shown that users tend to engage more with installed apps compared to visiting the official sites. Having a PWA as your product gives the users the look, feel and engagement of a normal app.</p>
<h3 id="heading-independent-connectivity">Independent Connectivity</h3>
<p>By keeping a user engaged to your app even while they are offline, provides a more consistent experience than dropping them back to a default offline page.</p>
<p>A good example to illustrate this will be that of a music app, your users should be able to access offline playback and listen to saved music even without internet connection. Another good example is twitter app, a user is able to go back a read through tweets which they might have missed.</p>
<h3 id="heading-discoverability">Discoverability</h3>
<p>Since most PWAs are converted websites, it is fair to make them discoverable on the search engines, this will help generate extra traffic to your app. This also acts as an advantage over native apps which can't be discovered over the search engines.</p>
<h3 id="heading-appearance">Appearance</h3>
<p>The appearance of the app should feel and look like that of a normal app, so be sure to include things like an app icon, this will help make it easily recognizable also things like splash screen will add the touch and feel of an app.</p>
<h3 id="heading-cross-platform">Cross Platform</h3>
<p>PWAs are developed as web app first, which means that they need to work on all browsers/systems and not just a selected few. Users should be able to use them in any browser before they decide to install them.</p>
<p>So folks! there you have it, the general info about PWAs. Along the way you might have noticed occasionally a comparison between PWAs and Native App and this might have confused you a bit, Well let's clear the airwaves by checking the comparison between the two to get a clear understanding.</p>
<h2 id="heading-differences-between-pwas-and-native-apps">Differences Between PWAs and Native Apps</h2>
<h3 id="heading-development-cost">Development Cost</h3>
<p>PWAs are cheaper to develop compared to Native AppsWhen you're developing a native app, you'll have to learn a certain programming language and then build a version of the app for each type of device, Android and iOS. On the other hand you can choose to hire a experienced professional to do the work for you which will even turn out to be more costly.</p>
<p>Down the road, you will also need resources to maintain and update the app, which means lots of money and time is required.</p>
<p>In the case of a PWA, you can have a single codebase for the different platforms. It's also time-saving since you will not need to develop it from scratch you can configure your current web site to fit.</p>
<p>And if you choose to hire developer it will only be one compared to native where you can hire up-to two depending on where you need your app.</p>
<h3 id="heading-discoverability-1">Discoverability</h3>
<p>Native apps cannot be indexed by the search engines, they can just be found through the App/Play store's website. You can make your app more discoverable on the App/Play store by using App Store Optimization(ASO), but that's another story.</p>
<p>Unlike native apps, PWAs work like websites so they can be indexed by search engines. This helps them rank better in search results.</p>
<h3 id="heading-safety">Safety</h3>
<p>Nowadays in order to run a website, it should be encrypted with a SSL certificate, this adds an extra layer of security. Now, as we already know PWAs are site converted into app which means they are more secure because they run on HTTPS. These are security protocols that allow safe exchange of data between client and server so that is doesn't get tampered with.</p>
<p>To secure your native apps, you need to implement various security measures, like multi-factor authentication and so on.</p>
<h3 id="heading-installation-and-download">Installation and Download</h3>
<p>Native apps need to be downloaded and installed from an app store. This requires some commitment from the user to do it from start to finish. Users have to pass and check multiple permissions before installing an app.</p>
<p>On the other hand, PWAs don't require any of those steps. From the browser you can bookmark it and add the app to your home screen with just a few taps.</p>
<h2 id="heading-benefits-of-pwas">Benefits of PWAs</h2>
<p>A lot of organizations both private and public are switching to PWAs not only because they are cheap to develop but also because they offer greater engagement.<br>Now let's look at a quick summary of the benefits of a PWA:</p>
<ul>
<li>They are responsive and work with many different screen sizes.</li>
<li>They can run on multiple platforms and any device with a modern web browser.</li>
<li>They function just like normal Native Apps.</li>
<li>The updates are independent, you don't need to visit the play store for an update.</li>
<li>They're built with common web technologies.</li>
<li>They're fast and lightweight.</li>
<li>They work offline unlike other sites.</li>
<li>They are discoverable via search engine.</li>
<li>They are easily installable.</li>
<li>Low maintenance cost.</li>
</ul>
<h2 id="heading-requirements-to-get-started-with-pwa-development">Requirements to Get Started with PWA Development</h2>
<p>It does not take much to get started building a PWA. You just need a few things and you are good to go.</p>
<p><strong>Tools</strong><br>The best known technology stack to develop PWAs is AngularJS. Speaking of Angular, <a target="_blank" href="https://allfront.io/blog/how-to-take-your-angular-apps-offline/">here</a> is a resourceful guide on how you can convert your already existing Angular app into PWA. Others stacks include ReactJS and Polymer.</p>
<p><strong>HTTPS</strong><br>You will need a server with a HTTPS connection. This makes sure your user's data is secure. It adds an extra layer of security to you site.</p>
<p><strong>Application Shell</strong><br>It provides a good first impression when your app loads. In simpler words this is what the user sees when they interact with your app for the first time.</p>
<p><strong>Service workers</strong><br>This is one of the key technologies behind PWAs. They help support your app work offline, and they perform advanced caching and run background tasks. Service workers can complete tasks even when your PWA is not running.Some other functions associated with Service Worker include:</p>
<ul>
<li>Sending push notification</li>
<li>Badging icons</li>
<li>Running background fetch tasks etc...</li>
</ul>
<p><strong>Manifest file</strong><br>This is a JSON file that is created with a <a target="_blank" href="https://app-manifest.firebaseapp.com/">Web App Manifest Generator</a>. This file contains the information that tells how your PWA should appear and function. It allows you to determine the name, description, icon, colors and other features of your PWA. Here's an example of a manifest file:</p>
<pre><code class="lang-json">{
<span class="hljs-attr">"short_name"</span>: <span class="hljs-string">"DevBlogger"</span>,  
<span class="hljs-attr">"name"</span>: <span class="hljs-string">"DevBlogger"</span>,  
<span class="hljs-attr">"description"</span>: <span class="hljs-string">"All dev stories under one roof"</span>,
<span class="hljs-attr">"theme_color"</span>: <span class="hljs-string">"#eb5252"</span>,  
<span class="hljs-attr">"background_color"</span>: <span class="hljs-string">"#000000"</span>,  
<span class="hljs-attr">"display"</span>: <span class="hljs-string">"fullscreen"</span>,  
<span class="hljs-attr">"Scope"</span>: <span class="hljs-string">"/"</span>,  <span class="hljs-attr">"orientation"</span>: <span class="hljs-string">"portrait"</span>,  
<span class="hljs-attr">"icons"</span>: [    
    {
        <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/android/android-launchericon-48-48.png"</span>,      
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,      
        <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"48x48"</span>
    },
    {      
        <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/android/android-launchericon-96-96.png"</span>,
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,      
        <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"96x96"</span>    
    },    
    {      
        <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/android/android-launchericon-192-192.png"</span>,
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>,      
        <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"192x192"</span>    
    }  
   ],  
       <span class="hljs-attr">"start_url"</span>: <span class="hljs-string">"index.html?utm_source=homescreen"</span>
  }
</code></pre>
<ul>
<li><strong>Audit your App</strong><br>This is possible using the Google Lighthouse tool. Google Lighthouse is a open-source software that anyone can use on any webpage. Google is a big champion of PWAs and pushes them as the future of the web. You can use Lighthouse to help you see how fast, accessible, and SEO readiness your PWA is.</li>
</ul>
<h2 id="heading-how-to-build-a-pwa">How to Build a PWA</h2>
<p>By following the steps below, you can easily create a fully functional PWA that offers an mazing user experience across all devices.</p>
<h3 id="heading-step-1-plan-your-app">Step 1 - Plan your app</h3>
<p>Before diving into development, you should consider the goals of your PWA, what features you want to include, priorities and user experience. You can create first design concepts and wireframes for the app to visualize the structure and layout.<br>In most scenarios, this is often referred to as a ‘<a target="_blank" href="https://allfront.io/blog/project-discovery-phase-guide/">discovery phase</a>’. You get the opportunity to ideate and gather user and stakeholder feedback as well as considering the functionalities of your to be product.</p>
<h3 id="heading-step-2-designing-the-user-interface">Step 2 - Designing the User Interface</h3>
<p>After getting everything right from planning, you can now proceed to designing the UI of your app. During this stage consider things like responsiveness, compatibility with different platforms etc.. Be sure to capture all details that are crucial to the user including their interaction and engagement during usage.</p>
<h3 id="heading-step-3-developing-the-front-end">Step 3 - Developing the Front-End</h3>
<p>Using the web technologies that is HTML, CSS, JavaScript and frameworks like Angular. React or Vue.js develop a visually appealing interface for the users. And always remember they key principle in development using this stack implement a mobile first approach while ensuring responsiveness for larger screens too.</p>
<h3 id="heading-step-4-implementing-service-workers">Step 4 - Implementing Service Workers</h3>
<p>As mentioned previously, service workers are a key component of PWAs. They are JavaScript files that run in the background, enabling offline functionality, push notifications, and caching. To make sure your PWA works to its fullest potential, you’ll need to register and implement a service worker. The way on how you can do this massively depends on which framework you are using.</p>
<h3 id="heading-step-5-adding-push-notifications">Step 5 - Adding Push Notifications</h3>
<p>Leverage the Push API and service workers to implement push notifications. Obtain the necessary user consent and use a push notification service to send notifications to users.</p>
<h3 id="heading-step-6-optimizing-performance">Step 6 - Optimizing Performance</h3>
<p>Optimization is a very important step in development in general. This is how you provide a seamless experience to your users. by ensuring you reduce loading times. by leveraging techniques such as code splitting and caching we should be able to achieve a fast and efficient operation for our PWA.</p>
<h3 id="heading-step-7-testing-and-debugging">Step 7 - Testing and Debugging</h3>
<p>Test your PWA on different devices, browsers and network condition to be sure that it meets the objective. Also be sure to gather user feedback and make necessary improvements when necessary.</p>
<h2 id="heading-resources-to-get-started-with-pwa-development">Resources to Get Started with PWA Development</h2>
<p>If you want to learn and move with the trend, finding resources to help you might be a bit tedious, to help you get started here are some of the best resources listed for you:<br><strong>Online Tutorials and Guides</strong></p>
<ul>
<li><a target="_blank" href="https://developers.google.com/web/progressive-web-apps">Google Developers - Progressive Web Apps</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps">MDN Web Docs - Progressive Web Apps</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/learn/front-end-development-libraries/">freeCodeCamp - Frontend Technologies</a></li>
<li><a target="_blank" href="https://web.dev/learn/pwa/">Learn PWA</a></li>
</ul>
<p><strong>Documentation and Reference Materials</strong></p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Workers API Documentation</a></li>
<li><a target="_blank" href="https://learn.microsoft.com/microsoft-edge/progressive-web-apps-chromium/?WT.mc_id=%3Fwt.mc_id%3Dstudentamb_223976">Progressive Web Apps (PWAs)</a></li>
</ul>
<p><strong>PWA Development Tools</strong></p>
<ul>
<li><a target="_blank" href="https://developers.google.com/web/tools/workbox">Workbox - Offline Caching and Service Worker Library</a></li>
<li><a target="_blank" href="https://www.pwabuilder.com/">PWA Builder - PWA Generation Platform</a></li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Keeping in mind that PWAs are new to the industry and haven't yet been fully utilized, they can be a great addition to add to your toolkit.</p>
<p>With the latest technologies and the right tools getting started with PWAs can ultimately increase sales and monetary gain for your product either as an individual or organization. With it's many features including they are fast, able to work offline, and also they perform like normal native apps. This offers your users a great experience and keeps them satisfied.</p>
<p>If you have read this far I really appreciate it.</p>
<p>Enjoy Coding ❤.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Protect Your PWA – Web App Security Best Practices ]]>
                </title>
                <description>
                    <![CDATA[ Hey, everybody! I'm Rahul. In today's world, web apps have become more prevalent because they work seamlessly on multiple devices. But as with any application, PWAs can be vulnerable to security threats, which can compromise user data and damage the ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-protect-your-pwa/</link>
                <guid isPermaLink="false">66c861a051f56da58aee7b78</guid>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rahul ]]>
                </dc:creator>
                <pubDate>Mon, 17 Apr 2023 21:28:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/pwa.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hey, everybody! I'm <a target="_blank" href="https://fueler.io/rahoool">Rahul</a>. In today's world, web apps have become more prevalent because they work seamlessly on multiple devices. But as with any application, PWAs can be vulnerable to security threats, which can compromise user data and damage the reputation.</p>
<p>It's natural to get excited about launching something new, but it's also important to remember the basics of website security. As a developer, you know it’s important to protect your web app from malicious users, attackers, hackers, and other cyber threats.</p>
<p>You've put in a lot of hard work building the perfect user experience and now it's time to keep it secure.</p>
<p>In this tutorial, we'll walk through some simple yet powerful strategies that help you protect your PWA. Let’s get started!</p>
<h1 id="heading-what-is-a-pwa">What is a PWA?</h1>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/undraw_progressive_app_m-9-ms.svg" alt="PWA Illustration by undraw.co" width="600" height="400" loading="lazy">
<em>PWA Illustration by undraw.co</em></p>
<p>Are you interested in bringing a native mobile app experience to your users without the headache of app development? Progressive Web App is the way.</p>
<p>PWAs are a type of web application that uses the latest web tech to deliver a user experience similar to that of a native mobile app.</p>
<p>They are built to take advantage of native mobile device features and work for apps regardless of whether the user has poor or no internet connection.</p>
<p>Additionally, PWAs are reliable, fast, and engaging — plus, they’re easier to develop and maintain than traditional apps.</p>
<p>The best part about PWAs is that they offer the same experience on all major browsers like Chrome, Firefox, Edge and Safari — so you don’t have to be concerned about compatibility issues.</p>
<p>Also, since PWAs don’t require installation from an app store, users don’t have to wait for their device manufacturer to approve or update them.</p>
<p>All these factors make PWAs ideal for businesses looking for cost-effective mobile solutions.</p>
<h1 id="heading-how-to-secure-a-pwa">How to Secure a PWA</h1>
<h2 id="heading-authentication-and-authorization-in-pwas">Authentication and Authorization in PWAs</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/undraw_authentication_re_svpt.svg" alt="Image" width="600" height="400" loading="lazy">
<em>authentication illustration by undraw.co</em></p>
<p>If you're building a Progressive Web App (PWA), you need to ensure that it is properly secured. In order to do this, you'll need to understand the basics of authentication and authorization.</p>
<p>Authentication is the process by which users prove their identity and verify that they have the proper authorization to access the app.</p>
<p>Authorization, on the other hand, ensures that those users have access only to areas and content that they are allowed to see, while enforcing restrictions on what they can do.</p>
<p>In order to implement effective authentication and authorization within your PWA, there are some best practices you should follow.</p>
<ol>
<li>Firstly, use secure passwords that are difficult for hackers or bots to guess or crack, as well as two-factor authentication (2FA) for added security.</li>
<li>Secondly, use roles-based access control (RBAC) for authorization. This establishes user permissions based on roles which can be quickly adjusted when needed.</li>
<li>Lastly, use HTTPS with SSL/TLS encryption for all web traffic from your PWA. This will ensure that all data sent between clients and servers is encrypted, protecting user information from malicious actors.</li>
</ol>
<p>It is important to keep in mind that authentication and authorization can also be vulnerable to attack by malicious users if not implemented correctly. So it is essential to stay aware of common attack vectors like SQL injection threats and cross-site scripting (XSS) attacks.</p>
<p>By implementing proper security measures such as input validation and penetration testing, you can protect your PWA from these threats.</p>
<h2 id="heading-how-to-use-https-and-ssltls-to-secure-your-pwas">How to Use HTTPS and SSL/TLS to Secure Your PWAs</h2>
<p>HTTPs and SSL/TLS are two of the most important tools when it comes to securing PWAs.</p>
<p>Let's see what exactly they are:</p>
<h3 id="heading-what-is-https">What is HTTPS?</h3>
<p>HTTPS stands for Hyper Text Transfer Protocol Secure. It's a protocol used for secure communication between a web server and a web browser, and is vital for protecting data from being intercepted by third parties.</p>
<p>To put it simply, HTTPS helps ensure that the data you send and receive from your PWA is always secure.</p>
<h3 id="heading-what-is-ssltls">What is SSL/TLS?</h3>
<p>SSL (Secure Sockets Layer) and <a target="_blank" href="https://www.freecodecamp.org/news/what-is-tls-transport-layer-security-encryption-explained-in-plain-english/">TLS (Transport Layer Security)</a> are cryptographic protocols that provide authentication, encryption and data integrity.</p>
<p>These protocols allow secure connections between web browsers, applications, and servers over the internet via an encrypted handshake process.</p>
<p>As such, they play an important role in ensuring that your web app's data remains safe and secure.</p>
<h3 id="heading-best-practices-for-implementing-https-and-ssltls-in-pwas">Best Practices for Implementing HTTPS and SSL/TLS in PWAs</h3>
<p>To protect your PWA effectively, you should make sure that you're following some best practices for implementing HTTPS and SSL/TLS in PWAs:</p>
<ol>
<li>Use HTTPS Everywhere: Make sure that all of your pages are served over HTTPS, rather than insecure HTTP connections. This will help ensure your users' data is protected while they're on your website or application.</li>
<li>Install an SSL Certificate: This will enable HTTPs connections on your site and provide authentication between the server hosting your PWA and the users visiting it.</li>
<li>Regularly Test Connections: Ensure that all of your connections are still secure by testing them regularly for any vulnerabilities or risks to security</li>
<li>Implement Certificate Pinning: Certificate pinning is an important security measure that involves binding a host domain to a specific SSL certificate. This prevents <strong>man-in-the-middle attacks</strong> and will help protect your PWA from malicious actors.</li>
<li>Keep Your Security Protocols Up to Date: Regularly update your web server's security protocols to ensure that you're always running the most secure version.</li>
<li>Configure Appropriate Firewall Rules: Configure your firewall rules to allow only secure connections and block any suspicious traffic. This will help protect your PWA from malicious attacks.</li>
</ol>
<p>Once you've implemented these best practices, you can feel pretty confident that your PWA is as secure as possible.</p>
<p>But, <a target="_blank" href="https://www.freecodecamp.org/news/attacks-on-ssl-tls-and-how-to-protect-your-system/">security isn't just about preventing malicious attacks</a>. It's about ensuring that your users' data is always kept safe and secure.</p>
<p>By taking the above steps, you can ensure that your PWA is always compliant with the latest security standards and guidelines. This will help protect your users' data from unauthorized access and potential security threats.</p>
<h2 id="heading-how-to-protect-against-cross-site-scripting-xss-and-cross-site-request-forgery-csrf">How to Protect against Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF)</h2>
<p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-protect-against-dom-xss-attacks/">Cross-site scripting</a> and <a target="_blank" href="https://www.freecodecamp.org/news/what-is-cross-site-request-forgery/">cross-site request forgery</a> are both security threats that can cause serious damage to PWAs.</p>
<p>XSS works by injecting malicious code into the browser via a vulnerable web application.</p>
<p>This code can be used to exfiltrate data from the PWA, like passwords or credit card numbers, or to execute any other malicious code.</p>
<p>CSRF, on the other hand, is a form of attack that manipulates a user's browser into sending forged requests without them knowing.</p>
<p>This can be used to execute actions within an application, such as transferring money or deleting data.</p>
<p>Fortunately, there are steps you can take to protect your PWA against XSS and CSRF attacks:</p>
<ul>
<li><strong>Sanitize all user input</strong>: Sanitizing user input is one of the best ways to protect against XSS and CSRF attacks. Make sure that you always validate and filter out any potentially harmful content before it’s added to your database.</li>
<li><strong>Use CAPTCHA protection</strong>: Adding CAPTCHA protection to web forms will help ensure that any requests from users are coming from real people rather than malicious bots or scripts.</li>
<li><strong>Use secure cookies</strong>: Secure cookies allow you to store data about your users in an encrypted format and limit access only to authorized users. This can help make sure that data isn’t exposed in case of a compromise.</li>
</ul>
<p>By taking these steps, you can help protect your PWA against XSS and CSRF attacks.</p>
<h2 id="heading-how-to-develop-a-content-security-policy-csp">How to Develop a Content Security Policy (CSP)</h2>
<p>Content Security Policy (CSP) is an important web security mechanism designed to prevent malicious attacks.</p>
<p>To put it simply, a CSP is a set of rules that specify what sources are allowed when loading resources such as scripts, images, and stylesheets. By defining the sources of these content types, CSP helps protect your applications from cross-site scripting (XSS) and data injection attacks.</p>
<p>By implementing a CSP on your PWA, you can add an extra layer of protection against malicious attacks.</p>
<p>Some best practices for implementing CSP include:</p>
<ul>
<li><strong>Defining allowed origins</strong>: This makes sure that only authorized sources can access content on your application.</li>
<li><strong>Whitelisting inline styles</strong>: When possible, use the nonce attribute to whitelist specific inline scripts and styles so that the code is only executed if the nonce matches what is specified in the policy.</li>
<li><strong>Disallowing unsafe methods</strong>: Include a directive that blocks dangerous methods such as eval() in order to protect against code injection attacks.</li>
<li><strong>Setting up reporting/logging</strong>: Configure reporting/logging so you can track incoming requests and flag any suspicious activity.</li>
</ul>
<p>By following these best practices and setting up CSP properly, you can effectively protect your PWAs from malicious attacks while ensuring they remain secure and performant.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In short, if you want to keep your web app secure, it’s important to follow best practice security protocols such as robust authentication and encryption, regular security patch updates, and secure APIs. It's also essential to have a secure development process in place to ensure that only the most secure code is being deployed.</p>
<p>Finally, user education is key when it comes to web apps. Providing users with a secure platform is only half the battle, as ultimately it’s up to the user to stay vigilant about their online security.</p>
<p>Read more on PWAs:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/what-are-progressive-web-apps/">What is a PWA? Progressive Web Apps for Beginners</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/">How to build a PWA from scratch with HTML, CSS, and JavaScript</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Dynamic Rick and Morty Wiki Web App with Next.js ]]>
                </title>
                <description>
                    <![CDATA[ Building web apps with dynamic APIs and server side rendering are a way to give people a great experience both with content and speed. How can we use Next.js to easily build those apps? What are we going to build? What is Next.js? Step 0: Setting up... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-dynamic-rick-and-morty-wiki-web-app-with-next-js/</link>
                <guid isPermaLink="false">66b8e351f805ffd579552eb2</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Applications ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Thu, 09 Jul 2020 05:49:56 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/07/nextjs.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building web apps with dynamic APIs and server side rendering are a way to give people a great experience both with content and speed. How can we use Next.js to easily build those apps?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-nextjs">What is Next.js?</a></li>
<li><a class="post-section-overview" href="#heading-step-0-setting-up-a-new-nextjs-app">Step 0: Setting up a new Next.js app</a></li>
<li><a class="post-section-overview" href="#heading-step-1-fetching-rick-and-morty-characters-with-an-api-in-nextjs">Step 1: Fetching Rick and Morty characters with an API in Next.js</a></li>
<li><a class="post-section-overview" href="#heading-step-2-displaying-rick-and-morty-characters-on-the-page">Step 2: Displaying Rick and Morty characters on the page</a></li>
<li><a class="post-section-overview" href="#heading-step-3-loading-more-rick-and-morty-characters">Step 3: Loading more Rick and Morty characters</a></li>
<li><a class="post-section-overview" href="#heading-step-4-adding-the-ability-to-search-for-rick-and-morty-characters">Step 4: Adding the ability to search for Rick and Morty characters</a></li>
<li><a class="post-section-overview" href="#heading-step-5-using-dynamic-routes-to-link-to-rick-and-morty-character-pages">Step 5: Using dynamic routes to link to Rick and Morty character pages</a></li>
<li><a class="post-section-overview" href="#heading-bonus-step-deploy-your-rick-and-morty-wiki-to-vercel">Bonus Step: Deploy your Rick and Morty wiki to Vercel!</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/iW39Merz0zE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We’re going to have some fun and build out a web app that serves as a basic wiki for Rick and Morty characters.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-morty-wiki-nextjs-demo-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Rick and Morty Wiki Demo</em></p>
<p>Our app is going to consist of a few things:</p>
<ul>
<li>A list of characters on the front page</li>
<li>A button that can load more characters, as the API is paginated</li>
<li>A search box to look up characters</li>
<li>A character page with basic details</li>
</ul>
<p>We’ll learn some concepts like:</p>
<ul>
<li>How to spin up a web app with <a target="_blank" href="https://nextjs.org/">Next.js</a></li>
<li>How to fetch and use data <a target="_blank" href="https://rickandmortyapi.com/api/character/">from an API</a></li>
<li>How to <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering">pre-render data from an API</a></li>
<li>How to set up <a target="_blank" href="https://nextjs.org/docs/routing/dynamic-routes">dynamic routing</a></li>
</ul>
<h2 id="heading-what-is-nextjs">What is Next.js?</h2>
<p><a target="_blank" href="https://nextjs.org/">Next.js</a> is a React framework from <a target="_blank" href="https://vercel.com/">Vercel</a>. It let’s you easily build lightweight dynamic web apps with a ton of modern features you’d expect out-of-the-box.</p>
<p>Vercel, the company that supports Next.js, is a service that allows you to automate continuous development pipelines to easily deploy web apps to the world. We’ll also use Vercel’s command line tool to optionally deploy our new wiki demo.</p>
<h2 id="heading-step-0-setting-up-a-new-nextjs-app">Step 0: Setting up a new Next.js app</h2>
<p>To get started, let’s spin up our Next.js project. We’ll use npm or yarn to get started:</p>
<pre><code class="lang-shell">yarn create next-app
# or
npx create-next-app
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/yarn-create-next-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new Next.js app</em></p>
<p>Once you run that command, it will ask you a few questions. I’m going to call my project <code>my-rick-and-morty-wiki</code>, but you can name it whatever you’d like.</p>
<p>It will then ask you which template to choose — go ahead and select the default template.</p>
<p>Finally, it will install all of the dependencies.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/successfully-created-next-app-terminal.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Successfully created a new Next.js app</em></p>
<p>When it’s finished, you can navigate to that new directory and run:</p>
<pre><code class="lang-shell">yarn dev
# or
npm run dev
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/develop-next-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Starting Next.js development server</em></p>
<p>You should now have a local server running at <a target="_blank" href="http://localhost:3000">http://localhost:3000</a>!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/default-template-next-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Default Next.js template</em></p>
<h2 id="heading-step-1-fetching-rick-and-morty-characters-with-an-api-in-nextjs">Step 1: Fetching Rick and Morty characters with an API in Next.js</h2>
<p>Now that we have our app set up, the first thing we need to actually build out our wiki is a list of characters.</p>
<p>To do this, we’re going to start off in our home page in <code>pages/index.js</code>.</p>
<p>Next.js scaffolds this page for us automatically. It’s the first page someone will hit on our website and has some basic features in the default template like a title, a simple grid, and some styles.</p>
<p>Currently, this page isn’t requesting any data. To get our characters, we’re going to jump right into requesting this server side.</p>
<p>To do this, Next.js allows us to export an async <code>getServerSideProps</code> function right next to our page, which it will use to inject our page with any data that we fetch.</p>
<p>Let’s start off by adding the following snippet above our <code>Home</code> function component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> defaultEndpoint = <span class="hljs-string">`https://rickandmortyapi.com/api/character/`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getServerSideProps</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(defaultEndpoint)
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      data
    }
  }
}
</code></pre>
<p>Here’s what we’re doing:</p>
<ul>
<li>We’re setting a variable called <code>defaultEndpoint</code> that simply defines our default API endpoint</li>
<li>We’re defining our <code>getServerSideProps</code> function that we’ll use to fetch our data</li>
<li>In that function, we first use the <code>fetch</code> API to make a request to our endpoint</li>
<li>With it’s response, we run the <code>json</code> method so that we can grab the output in JSON format</li>
<li>Finally, we return an object where we make our <code>data</code> available as a prop in the <code>props</code> property</li>
</ul>
<p>Now that we’re making that request, we need to make it available to use.</p>
<p>Our <code>data</code> is made available as a prop, so let’s create an argument in our <code>Home</code> component function to grab that:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params">{ data }</span>) </span>{
</code></pre><p>To test this, we can use <code>console.log</code> to see the results:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params">{ data }</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'data'</span>, data);
</code></pre>
<p>And once we save and reload the page, we can now see our results!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/nextjs-app-logging-server-rendered-data.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logging out Rick and Morty character data in Next.js app</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-rick-and-morty-wiki/commit/3850e08e47654053d33f8440557e882e3579b335">Follow along with the commit!</a></p>
<h2 id="heading-step-2-displaying-rick-and-morty-characters-on-the-page">Step 2: Displaying Rick and Morty characters on the page</h2>
<p>Now that we have our character data, let’s actually display it on our page.</p>
<p>To start, I’m going to make a few tweaks. I’m going to update:</p>
<ul>
<li>The <code>&lt;h1&gt;</code> title to “Wubba Lubba Dub Dub!”</li>
<li>The <code>&lt;p&gt;</code> description to “Rick and Morty Character Wiki”</li>
</ul>
<p>I’m also going to update the contents of <code>&lt;div clasName=“grid”</code> to:</p>
<pre><code class="lang-jsx">&lt;ul className=<span class="hljs-string">"grid"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://nextjs.org/docs"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>My Character<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
&lt;/ul&gt;
</code></pre>
<p>What I’m doing here:</p>
<ul>
<li>I’m making the <code>&lt;div&gt;</code> a list as that will be better for accessibility</li>
<li>I’m making the <code>&lt;li&gt;</code> of the <code>&lt;ul&gt;</code> the <code>card</code></li>
<li>And just changing the <code>&lt;h3&gt;</code> to “My Character” temporarily</li>
</ul>
<p>To make sure our new <code>&lt;ul&gt;</code> doesn’t mess up the layout with it’s default styles, let’s also add the following to the bottom of the <code>.grid</code> CSS rules:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">list-style</span>: <span class="hljs-selector-tag">none</span>;
<span class="hljs-selector-tag">margin-left</span>: 0;
<span class="hljs-selector-tag">padding-left</span>: 0;
</code></pre>
<p>And now if we look at the page, we should see our basic changes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/basic-wiki-page-with-title.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Updated title in Rick and Morty Wiki</em></p>
<p>Next, let’s make our grid load our characters.</p>
<p>At the top of our <code>Home</code> component function, let’s add:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { results = [] } = data;
</code></pre>
<p>That will destructure our results array from our data object.</p>
<p>Next, let’s update our grid code:</p>
<pre><code class="lang-jsx">&lt;ul className=<span class="hljs-string">"grid"</span>&gt;
  {results.map(<span class="hljs-function"><span class="hljs-params">result</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> { id, name } = result;
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{ name }<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
    )
  })}
&lt;/ul&gt;
</code></pre>
<p>Here’s what we’re doing:</p>
<ul>
<li>We’re using the <code>map</code> method to create a new list element for each of our results (or characters)</li>
<li>Inside of that, we’re grabbing the <code>id</code> and <code>name</code> from each  character result</li>
<li>We’re using the ID as a <code>key</code> for our list element to make React happy</li>
<li>We’re updating our header with the <code>name</code></li>
</ul>
<p>And once you save and reload the page, we should now see a new list of our characters from the API!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-with-character-names.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Dynamic list of Rick and Morty character names</em></p>
<p>We can also add an image for each character.</p>
<p>First, inside of our grid, let’s update our destructure statement to grab the image URL:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { id, name, image } = result;
</code></pre>
<p>Next, let’s add the image above our header:</p>
<pre><code class="lang-jsx">&lt;img src={image} alt={<span class="hljs-string">`<span class="hljs-subst">${name}</span> Thumbnail`</span>} /&gt;
</code></pre>
<p>And now each of our characters also shows their picture!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-with-character-names-and-pictures.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding character images on Rick and Morty Wiki</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-rick-and-morty-wiki/commit/fd959ac66a51900d2fcff9130925d8979ab8db32">Follow along with the commit!</a></p>
<h2 id="heading-step-3-loading-more-rick-and-morty-characters">Step 3: Loading more Rick and Morty characters</h2>
<p>Now if you notice, when we load the page, we only get a certain number of results. By default, the API won’t return the entire list of characters, which makes sense, because it’s really long!</p>
<p>Instead, it uses pagination and provides us with the “next” endpoint, or the next page of results, that will allow us to load in more results.</p>
<p>To start, we’re going to use React’s <code>useState</code> hook to store our results in state. We’ll then have the ability to update that state with more results.</p>
<p>First, let’s import <code>useState</code> from React:</p>
<pre><code><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
</code></pre><p>Next, let’s create our state by first renaming our original <code>results</code> variable and setting up our <code>useState</code> instance:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { <span class="hljs-attr">results</span>: defaultResults = [] } = data;
<span class="hljs-keyword">const</span> [results, updateResults] = useState(defaultResults);
</code></pre>
<p>If you save that and reload the page, you shouldn’t notice anything different yet.</p>
<p>Next, we want to be able to understand in our application what our current endpoint we’ve made a request is, what the next endpoint is, what the previous endpoint is, and how we can update all of that.</p>
<p>To do this, we’re going to create more state. First, we want to update our destructuring statement with our <code>data</code> to get the <code>info</code> value:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { info, <span class="hljs-attr">results</span>: defaultResults = [] } = data;
</code></pre>
<p>Next, let’s set up some state using that:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> [page, updatePage] = useState({
  ...info,
  <span class="hljs-attr">current</span>: defaultEndpoint
});
</code></pre>
<p>Here, we’re:</p>
<ul>
<li>Creating a new <code>page</code> state that we can use to get our <code>prev</code> and <code>next</code> values</li>
<li>We’re also creating a new value called <code>current</code> we’ll we start off by using our <code>defaultEndpoint</code>, which was the request made on the server</li>
</ul>
<p>The idea here, is when we want to load more results, we’re going set up code to watch the value of <code>current</code> and update that value with the <code>next</code>, so when it  changes, we’ll make a new request.</p>
<p>To do that, let’s add a <code>useEffect</code> hook to make that request:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { current } = page;

useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">if</span> ( current === defaultEndpoint ) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">request</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(current)
    <span class="hljs-keyword">const</span> nextData = <span class="hljs-keyword">await</span> res.json();

    updatePage({
      current,
      ...nextData.info
    });

    <span class="hljs-keyword">if</span> ( !nextData.info?.prev ) {
      updateResults(nextData.results);
      <span class="hljs-keyword">return</span>;
    }

    updateResults(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> [
        ...prev,
        ...nextData.results
      ]
    });
  }

  request();
}, [current]);
</code></pre>
<p>Here’s what’s going on:</p>
<ul>
<li>First, we’re destructuring the <code>current</code> value from `page</li>
<li>We’re creating a <code>useEffect</code> hook that uses <code>current</code> as a dependency. If they value changes, the hook will run</li>
<li>If our <code>current</code> value is the same as <code>defaultEndpoint</code>, we don’t run the code, as we already have our request data. Prevents and extra on load request</li>
<li>We create an async function that we’re able to run. This allows us to use <code>async/await</code> inside of our <code>useEffect</code> hook</li>
<li>We make the request to the <code>current</code> endpoint. With that successful request, we update the <code>page</code> state with the new <code>info</code> like the new <code>prev</code> and <code>next</code> value</li>
<li>If our request does not have a previous value, that means it’s the first set of results for the given request, so we should completely replace our results to start from scratch</li>
<li>If we do have a previous value, concatenate our new results to the old, as that means we just requested the next page of results</li>
</ul>
<p>Again, if you save and reload the page, this still shouldn’t do anything and your page should be where it was before.</p>
<p>Finally, we’re going to create a Load More button and use it to update the <code>current</code> value to fire off a new request when we want a new page.</p>
<p>To do that, let’s first add a new button below our grid:</p>
<pre><code class="lang-jsx">&lt;p&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
&lt;/p&gt;
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-load-more-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Added Load More button to Rick and Morty Wiki character list</em></p>
<p>Now we want something to happen when we click that button, so first add an event handler:</p>
<pre><code class="lang-jsx">&lt;button onClick={handleLoadMore}&gt;Load More&lt;/button&gt;
</code></pre>
<p>Then above the component return statement, let’s add that function:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleLoadMore</span>(<span class="hljs-params"></span>) </span>{
  updatePage(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> {
      ...prev,
      <span class="hljs-attr">current</span>: page?.next
    }
  });
}
</code></pre>
<p>When triggered with our button click, this function will update our <code>page</code> state with a new <code>current</code> value, specifically with the <code>next</code> value which is the endpoint to fetch our next page of results.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-morty-wiki-load-more-characters.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Loading more results in Rick and Morty Wiki</em></p>
<p>And when we save and reload the page, it does just that!</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-rick-and-morty-wiki/commit/157eda9c3a93eb79e6e063eaa60f7abe82246fc5">Follow along with the commit!</a></p>
<h2 id="heading-step-4-adding-the-ability-to-search-for-rick-and-morty-characters">Step 4: Adding the ability to search for Rick and Morty characters</h2>
<p>One of the features out Rick and Morty API provides is the ability to filter results — so basically the ability to search. So let’s add that as a feature.</p>
<p>First, we need a search form. Let’s add the following snippet under the description paragraph:</p>
<pre><code class="lang-jsx">&lt;form className=<span class="hljs-string">"search"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"query"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"search"</span> /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Search<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
&lt;/form&gt;
</code></pre>
<p>Next, let’s add these styles to the bottom of the first <code>&lt;style jsx&gt;</code> block:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.search</span> <span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">margin-right</span>: .<span class="hljs-number">5em</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">600px</span>) {
  <span class="hljs-selector-class">.search</span> <span class="hljs-selector-tag">input</span> {
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin-bottom</span>: .<span class="hljs-number">5em</span>;
  }

  <span class="hljs-selector-class">.search</span> <span class="hljs-selector-tag">input</span>,
  <span class="hljs-selector-class">.search</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  }
}
</code></pre>
<p>That’s going to give some spacing to our search input and button as well as make it mobile friendly. Feel free to add more styles if you’d like.</p>
<p>And if we save and refresh our page, we have a simple form.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-character-wiki-search-form.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Added search form to Rick and Morty Wiki</em></p>
<p>It doesn’t do anything yet, so let’s make it search when submit the form.</p>
<p>To start, let’s add an <code>onSubmit</code> attribute to our form:</p>
<pre><code class="lang-jsx">&lt;form className=<span class="hljs-string">"search"</span> onSubmit={handleOnSubmitSearch}&gt;
</code></pre>
<p>And to go with that, let’s define our submit function above our return statement:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleOnSubmitSearch</span>(<span class="hljs-params">e</span>) </span>{
  e.preventDefault();

  <span class="hljs-keyword">const</span> { currentTarget = {} } = e;
  <span class="hljs-keyword">const</span> fields = <span class="hljs-built_in">Array</span>.from(currentTarget?.elements);
  <span class="hljs-keyword">const</span> fieldQuery = fields.find(<span class="hljs-function"><span class="hljs-params">field</span> =&gt;</span> field.name === <span class="hljs-string">'query'</span>);

  <span class="hljs-keyword">const</span> value = fieldQuery.value || <span class="hljs-string">''</span>;
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`https://rickandmortyapi.com/api/character/?name=<span class="hljs-subst">${value}</span>`</span>;

  updatePage({
    <span class="hljs-attr">current</span>: endpoint
  });
}
</code></pre>
<p>Here’s what we’re  doing:</p>
<ul>
<li>First we’re preventing default behavior from the form submission to prevent the page from reloading</li>
<li>Next we grab the current target, which is our form</li>
<li>We grab the fields from the form by using the elements property. We also turn this into an array so it’s easy to work with</li>
<li>We search those fields for our query input</li>
<li>We grab the value of that input</li>
<li>We create a new endpoint where we filter by name using that query value</li>
<li>Finally, we update our <code>current</code> property in our page state to trigger a new request to that endpoint</li>
</ul>
<p>And once you save that and reload the page, you can now give search a try. You should be able to type in a name like “rick”, hit enter or click the search button, and you should now see filtered results with the various ricks across the universe!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-search-rick.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Searching for Rick on Rick and Morty Wiki</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-rick-and-morty-wiki/commit/f365d2bc7fc3ca48c2ad693d457a6b6984ea67c3">Follow along with the commit!</a></p>
<h2 id="heading-step-5-using-dynamic-routes-to-link-to-rick-and-morty-character-pages">Step 5: Using dynamic routes to link to Rick and Morty character pages</h2>
<p>Now that we have all of our characters, we want to be able to click into those characters and display some additional details. To do that, we’re  going to make use of Next.js’s dynamic routes.</p>
<p>The first thing we need to do is properly configure our directory structure so Next.js recognizes the dynamic path. In order to set up a dynamic route, we need to create our folder exactly like:</p>
<pre><code>- pages
-- character
--- [id]
-- index.js
</code></pre><p>Yes, that means you’re literally creating a folder with the name of <code>[id]</code>, that’s not meant to be replaced. Next.js recognizes that pattern and will let us use that to create a dynamic route.</p>
<p>To make creating the page easier, we’re going to simply duplicate our homepage by copying our <code>pages/index.js</code> file into our next directly.</p>
<p>So we should now have a new page at <code>pages/character/[id]/index.js</code>.</p>
<p>Next, let’s remove a bunch of stuff so we can get to a good starting point:</p>
<ul>
<li>Remove everything above the <code>return</code> statement in our page’s function component</li>
<li>Rename the function component Character</li>
<li>Remove the <code>useState</code> and <code>useEffect</code> imports</li>
<li>Remove the description, search form, grid, and load more button</li>
<li>Optional: remove the footer</li>
</ul>
<p>Once you’re done, the top of our page’s function component should look like:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Character</span>(<span class="hljs-params">{ data }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Create Next App<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.ico"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Head</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title"</span>&gt;</span>
          Wubba Lubba Dub Dub!
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
</code></pre>
<p>While there is some CSS we don’t need, we’re going to leave it all there for this demo. Feel free to clean some of that out later.</p>
<p>If you navigate manually to /character/1, you should now see a simple page with just a title:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/new-character-page-basic-title.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Simple character page</em></p>
<p>Next, let’s update the data we’re fetching. We can reuse most of the code in our <code>getServerSideProps</code> function.</p>
<p>We’re going to add a new argument to that <code>getServerSideProps</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getServerSideProps</span>(<span class="hljs-params">{ query }</span>) </span>{
</code></pre>
<p>When our page is rendered, Next.js injects data into our page and the <code>getServerSideProps</code> function about the environment. Here, we’re destructuring that data to grab the <code>query</code> object which will include any dynamic routing attributes, such as the <code>[id]</code> we’re setting in the route.</p>
<p>Next, at the top of the <code>getServerSideProps</code> function, let’s destructure the ID:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { id } = query;
</code></pre>
<p>And finally let’s use that ID to dynamically create an endpoint we’ll use to fetch our character data:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${defaultEndpoint}</span><span class="hljs-subst">${id}</span>`</span>);
</code></pre>
<p>Here, we’re using our character endpoint and appending the dynamic ID of our URL to the end of the URL.</p>
<p>To test this out, let’s add a <code>console.log</code> to the top of the <code>Character</code> function:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Character</span>(<span class="hljs-params">{ data }</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'data'</span>, data);
</code></pre>
<p>And if we hit save and reload our page, we should now see the user details about character number 1 logged out, which is Rick Sanchez!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-character-page-console-log-data.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logging out individual Rick and Morty character data</em></p>
<p>So we have the data, let’s add it to our page.</p>
<p>At the top of the character function, let’s add this destructure statement:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { name, image, gender, location, origin, species, status } = data;
</code></pre>
<p>This gives us a bunch of attributes we’re getting right from that data object we saw logged out.</p>
<p>To use that, we can start by updating the title to that name:</p>
<pre><code class="lang-jsx">&lt;title&gt;{ name }&lt;/title&gt;
</code></pre>
<p>Also the <code>&lt;h1&gt;</code>:</p>
<pre><code class="lang-jsx">&lt;h1 className=<span class="hljs-string">"title"</span>&gt;{ name }&lt;/h1&gt;
</code></pre>
<p>At this point, we should now dynamically see Rick’s name.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-character-page-dynamic-title.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Dynamic Rick and Morty character page title</em></p>
<p>Next, let’s add this block below our <code>&lt;h1&gt;</code> to include more of our character details:</p>
<pre><code class="lang-jsx">&lt;div className=<span class="hljs-string">"profile"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"profile-image"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{name}</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"profile-details"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Character Details<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Name:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> { name }
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Status:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> { status }
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Gender:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> { gender }
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Species:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> { species }
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Location:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> { location?.name }
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Originally From:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> { origin?.name }
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/div&gt;
</code></pre>
<p>Here we’re using our characters <code>image</code> to display a picture of our character and other various metadata to add Character Details.</p>
<p>We can follow that up by adding this snippet of CSS to our styles:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.profile</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2em</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">600px</span>) {
  <span class="hljs-selector-class">.profile</span> {
    <span class="hljs-attribute">flex-direction</span>: column;
  }
}

<span class="hljs-selector-class">.profile-image</span> {
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">2em</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">600px</span>) {
  <span class="hljs-selector-class">.profile-image</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  }
}
</code></pre>
<p>And now we have our character bio!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-character-page-rick-sanchez.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Dynamic Rick Sanchez characer page</em></p>
<p>So a quick recap, we have our new dynamic page. We can go to <code>/character/1</code> or any ID to see a specific character. Let’s now update our homepage to link to these pages.</p>
<p>Back on <code>pages/index.js</code>, our homepage, let’s first import the <code>Link</code> component from Next.js:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">'next/link'</span>
</code></pre>
<p>Next, inside of our grid where we map through our list of results, let’s use our <code>&lt;Link&gt;</code> component and update our code:</p>
<pre><code class="lang-jsx">&lt;li key={id} className=<span class="hljs-string">"card"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/character/[id]"</span> <span class="hljs-attr">as</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">character</span>/${<span class="hljs-attr">id</span>}`}&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">name</span>} <span class="hljs-attr">Thumbnail</span>`} /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{ name }<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span></span>
&lt;/li&gt;
</code></pre>
<p>Here’s what we’re doing:</p>
<ul>
<li>First we’re wrapping our <code>&lt;a&gt;</code> element with a <code>&lt;Link&gt;</code> component</li>
<li>We add a <code>href</code> and the <code>as</code> properties to describe to Next.js what page we want to link to. We need to use the <code>as</code> property as it’s a dynamic link</li>
<li>We remove the <code>href</code> from our <code>&lt;a&gt;</code> element as it’s now being applied to the <code>&lt;Link&gt;</code> element</li>
</ul>
<p>If we save and reload our homepage, we’ll notice that nothing changed, but when we click any of our characters, we now go to their bio page!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-navigate-to-jerry-smith.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Navigating to Jerry Smith character page on Rick and Morty Wiki</em></p>
<p>Finally, let’s also add a button to our character bio page that links back to our homepage to make it easier to navigate.</p>
<p>First, let’s import the <code>Link</code> component:</p>
<pre><code><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">'next/link'</span>;
</code></pre><p>At the bottom of our <code>&lt;main&gt;</code> tag below our <code>.profile</code> div, let’s add this code:</p>
<pre><code>&lt;p className=<span class="hljs-string">"back"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>
      Back to All Characters
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span></span>
&lt;/p&gt;
</code></pre><p>And we can add the following basic styles to simply make it look like a link:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.back</span> <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">color</span>: blue;
  <span class="hljs-attribute">text-decoration</span>: underline;
}
</code></pre>
<p>And if we reload the page, we now have link that we can click to go back to the main page with all of our characters!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-back-to-all-characters.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Back to All Characters link on Rick and Morty character page</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-rick-and-morty-wiki/commit/61ec2f5b2092278dc3983c339fa4e556a5c7862c">Follow along with the commit!</a></p>
<h2 id="heading-bonus-step-deploy-your-rick-and-morty-wiki-to-vercel">Bonus Step: Deploy your Rick and Morty wiki to Vercel!</h2>
<p>Because we’re using Next.js, Vercel makes it super simple to deploy our app.</p>
<p>To do this, we need to <a target="_blank" href="https://vercel.com/download">install the Vercel CLI</a>. We can do that by installing it as an npm module globally:</p>
<pre><code class="lang-shell">yarn global add vercel
# or 
npm i -g vercel
</code></pre>
<p>Now, you  can run the <code>vercel</code> command in your terminal.</p>
<p>The first time you run this, you’ll be prompted to log in. You’ll want to use your Vercel account to do this. If you don’t have one, you’ll want to <a target="_blank" href="https://vercel.com/signup">sign up for a free account</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/vercel-cli-login.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logging into Vercel CLI</em></p>
<p>With the Vercel CLI installed, we can simply run <code>vercel</code> in our project directory, fill out a few questions, and it will automatically deploy!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/vercel-cli-deploy.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Deploying app with Vercel CLI</em></p>
<p>You can use pretty much all of the defaults, though you will probably need to use a different project name than I’m using.</p>
<p>But once finished, we now have successfully deployed our new app to Vercel!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/rick-and-morty-wiki-deployed-to-vercel.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Finished Rick and Morty Wiki app</em></p>
<h2 id="heading-what-else-can-we-do">What else can we do?</h2>
<h3 id="heading-more-dynamic-pages">More dynamic pages</h3>
<p>Every time you make a request to a character, the API returns other endpoints that you can use such as locations and episodes. We can utilize those endpoints and create new dynamic pages, just like our dynamic character profile pages, to allow people to see more information about a specific location or episode.</p>
<h3 id="heading-add-some-styles">Add some styles</h3>
<p>We stuck with some of the basic styles that Next.js included and added some basic ones just for demonstration purposes. But now that you’re finished, you can have some fun and make it your own!</p>
<h3 id="heading-add-character-filters">Add character filters</h3>
<p>In addition to filtering by name, the <a target="_blank" href="https://rickandmortyapi.com/documentation/#filter-characters">API also supports filtering by status</a>. By adding a <code>status</code> parameter to the endpoint URL, just like our <code>name</code> parameter, you can add a new filter to make it easier to find characters that are still alive or not.</p>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to implement server-side rendering in your React app in three simple steps ]]>
                </title>
                <description>
                    <![CDATA[ By Rohit Kumar Here’s what we will build in this tutorial: a nice React card like this one. In this tutorial, we’ll use server-side rendering to deliver an HTML response when a user or crawler hits a page URL. We’ll handle the latter requests on the... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/server-side-rendering-your-react-app-in-three-simple-steps-7a82b95db82e/</link>
                <guid isPermaLink="false">66c35e94c7095d76345eaff8</guid>
                
                    <category>
                        <![CDATA[ ES6 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 29 Feb 2020 11:41:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9c5d740569d1a4ca31bc.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_wk04sWGQkw36_XLFvPACrA-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>By Rohit Kumar</p>
<p>Here’s what we will build in this tutorial: a nice React card like this one.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_wk04sWGQkw36_XLFvPACrA-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this tutorial, we’ll use server-side rendering to deliver an HTML response when a user or crawler hits a page URL. We’ll handle the latter requests on the client side.</p>
<p>Why do we need it?</p>
<p>Let me guide you to the answer.</p>
<h2 id="heading-whats-the-difference-between-client-side-rendering-and-server-side-rendering">What’s the difference between client-side rendering and server-side rendering?</h2>
<p>In <strong>Client-side rendering,</strong> your browser downloads a minimal HTML page. It renders the JavaScript and fills the content into it.</p>
<p><strong>Server-side rendering,</strong> on the other hand, renders the React components on the server. The output is HTML content.</p>
<p>You can combine these two to create an isomorphic app.</p>
<h2 id="heading-cons-of-rendering-react-on-the-server">Cons of Rendering React on the Server</h2>
<ul>
<li>SSR can improve performance if your application is small. But it can also degrade performance if it is heavy.</li>
<li>It increases response time (and it can be worse if the server is busy).</li>
<li>It increases response size, which means the page takes longer to load.</li>
<li>It increases the complexity of the application.</li>
</ul>
<h2 id="heading-when-should-you-use-server-side-rendering">When should you use Server Side Rendering?</h2>
<p>Despite these consequences of SSR, there are some situations in which you can and should use it.</p>
<h3 id="heading-1-seo">1. SEO</h3>
<p>Every website wants to appear in searches. Correct me if I’m wrong.</p>
<p>Unfortunately, Search engine crawlers do not yet understand/render JavaScript.</p>
<p>This means they see a blank page, no matter how helpful your site is.</p>
<p>Many folks say that Google’s crawler <a target="_blank" href="https://www.searchenginejournal.com/googles-search-crawlers-natively-render-javascript-based-pages/226313/">now renders JavaScript</a>.</p>
<p>To test this, I deployed the app on Heroku. Here is what I saw on the Google Search Console:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*KgOtUd6XBbeZvR1FDBGcXA.png" alt="Image" width="800" height="537" loading="lazy">
<em>Google’s crawler does not render React</em></p>
<p>A blank page.</p>
<p>This was the biggest reason I explored server-side rendering. Especially when it is a <a target="_blank" href="https://yoast.com/what-is-cornerstone-content/">cornerstone page</a> such as a landing page, blog, and so on.</p>
<p>To verify if Google renders your site, visit:</p>
<p>Search Console Dashboard &gt; Crawl &gt; Fetch as Google. Enter the page URL or leave it empty for the homepage.</p>
<p>Select FETCH AND RENDER. Once complete, click to see the result.</p>
<h3 id="heading-2-improve-performance">2. Improve performance</h3>
<p>In SSR, the application performance depends on the server’s resources and user’s network speed. This makes it very useful for content-heavy sites.</p>
<p><em>For Example</em>, say that you have a medium-price mobile phone with slow internet speed. You try to access a site that downloads 4MB of data before you can see anything.</p>
<p>Would you be able to see anything on your screen within 2–4 seconds?</p>
<p>Would you visit that site again?</p>
<p>I don’t think you would.</p>
<p>Another major improvement is in <a target="_blank" href="https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive">First User Interaction Time</a>. This is the difference in time from when a user hits the URL to when they see content.</p>
<p>Here’s the comparison. I tested it on a development Mac.</p>
<h4 id="heading-react-rendered-on-server">React Rendered on Server</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*kYMHoa7OemCHA_KBzJ1w-w.png" alt="Image" width="800" height="347" loading="lazy">
<em>SSR performance report (Chrome)</em></p>
<p>The first interaction time is 300ms. Hydrate finishes at 400ms. The load event exits at 500ms approximately. You can see this by checking out the image above.</p>
<h4 id="heading-react-rendered-on-clients-browser">React Rendered on Client’s Browser</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*wquRCRboPDi7Ix2HAxvCAA.png" alt="Image" width="800" height="342" loading="lazy">
<em>Client side performance report (Chrome)</em></p>
<p>The first interaction time is 400ms. The load event exits at 470ms.</p>
<p>The result speaks for itself. There’s a 100ms difference in the First User Interaction Time for such a small app.</p>
<h3 id="heading-how-does-it-work-4-simple-steps">How does it Work? — (4 Simple Steps)</h3>
<ul>
<li>Create a fresh Redux Store on every request.</li>
<li>Optionally dispatch some actions.</li>
<li>Get the state out of the Store and perform SSR.</li>
<li>Send the state obtained in the previous step along with the response.</li>
</ul>
<p>We will use the state passed in the response for creating the initial state on client-side.</p>
<p>Before you get started, <a target="_blank" href="https://github.com/Rohitkrops/ssr">clone/download the complete example from Github</a> and use it for reference.</p>
<h3 id="heading-getting-started-by-setting-up-our-app">Getting Started by Setting up our App</h3>
<p>First, open your favourite editor and shell. Create a new folder for your application. Let’s start.</p>
<pre><code class="lang-bash">npm init --yes
</code></pre>
<p>Fill in the details. After <code>package.json</code> is created, copy the dependencies and scripts below into it.</p>
<p>Install all dependencies by running:</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>You need to configure Babel and webpack for our build script to work.</p>
<p>Babel transforms ESM and react into Node and browser-understood code.</p>
<p>Create a new file <code>.babelrc</code> and put the line below in it.</p>
<pre><code class="lang-js">{
  <span class="hljs-string">"presets"</span>: [<span class="hljs-string">"@babel/env"</span>, <span class="hljs-string">"@babel/react"</span>]
}
</code></pre>
<p>webpack bundles our app and its dependencies into a single file. Create another file <code>webpack.config.js</code> with the following code in it:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);<span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-attr">entry</span>: {
        <span class="hljs-attr">client</span>: <span class="hljs-string">'./src/client.js'</span>,
        <span class="hljs-attr">bundle</span>: <span class="hljs-string">'./src/bundle.js'</span>
    },
    <span class="hljs-attr">output</span>: {
        <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">'assets'</span>),
        <span class="hljs-attr">filename</span>: <span class="hljs-string">"[name].js"</span>
    },
    <span class="hljs-attr">module</span>: {
        <span class="hljs-attr">rules</span>: [
            { <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.js$/</span>, exclude: <span class="hljs-regexp">/node_modules/</span>, loader: <span class="hljs-string">"babel-loader"</span> }
        ]
    }
}
</code></pre>
<p>The build process output’s two files:</p>
<ol>
<li><code>assets/bundle.js</code> — pure client side app.</li>
<li><code>assets/client.js</code> — client side companion for SSR.</li>
</ol>
<p>The <code>src/</code> folder contains the source code. The Babel compiled files go into <code>views/</code>. <code>views</code> directory will be created automatically if not present.</p>
<h3 id="heading-why-do-we-need-to-compile-source-files">Why do we need to compile source files?</h3>
<p>The reason is the syntax <a target="_blank" href="http://jsmodules.io/cjs.html">difference between ESM &amp; CommonJS</a>. While writing React and Redux, we heavily use import and export in all files.</p>
<p>Unfortunately, they don’t work in Node. Here comes Babel to rescue. The script below tells Babel to compile all files in the <code>src</code> directory and put the result in <code>views.</code></p>
<pre><code class="lang-json"><span class="hljs-string">"babel"</span>: <span class="hljs-string">"babel src -d views"</span>,
</code></pre>
<p>Now, Node can run them.</p>
<h3 id="heading-copy-precoded-amp-static-files">Copy Precoded &amp; Static files</h3>
<p>If you have already cloned the repository, copy from it. Otherwise d<a target="_blank" href="https://www.dropbox.com/s/2iijlivmlye6pqp/ssr-static.zip?dl=0">ownload ssr-static.zip file from Dropbox</a>. Extract it and keep these three folders inside your app directory. Here’s what they contain.</p>
<ol>
<li>React <code>App</code> and components resides in <code>src/components</code>.</li>
<li>Redux files in <code>src/redux/</code>.</li>
<li><code>assets/ &amp; media/</code>: Contain static files such as <code>style.css</code> and images.</li>
</ol>
<h3 id="heading-server-side">Server Side</h3>
<p>Create two new files named <code>server.js</code> and <code>template.js</code> inside the <code>src/</code> folder.</p>
<h3 id="heading-1-srcserverjs">1. src/server.js</h3>
<p>Magic happens here. This is the code you’ve been searching for.</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> { renderToString } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/server'</span>;
<span class="hljs-keyword">import</span> { Provider } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-redux'</span>;
<span class="hljs-keyword">import</span> configureStore <span class="hljs-keyword">from</span> <span class="hljs-string">'./redux/configureStore'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/app'</span>;

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params">initialState</span>) </span>{
  <span class="hljs-comment">// Model the initial state  </span>
  <span class="hljs-keyword">const</span> store = configureStore(initialState);
  <span class="hljs-keyword">let</span> content = renderToString(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span> &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>);
  <span class="hljs-keyword">const</span> preloadedState = store.getState();
  <span class="hljs-keyword">return</span> {
    content,
    preloadedState
  };
};
</code></pre>
<p>Instead of rendering our app, we need to wrap it into a function and export it. The function accepts the initial state of the application.</p>
<p>Here’s how it works.</p>
<ol>
<li>Pass <code>initialState</code> to <code>configureStore()</code>. <code>configureStore()</code>returns a new Store instance. Hold it inside the <code>store</code> variable.</li>
<li>Call <code>renderToString()</code> method, providing our App as input. It renders our app on the server and returns the HTML produced. Now, the variable <code>content</code> stores the HTML.</li>
<li>Get the state out of Redux Store by calling <code>getState()</code> on <code>store</code>. Keep it in a variable <code>preloadedState</code>.</li>
<li>Return the <code>content</code> and <code>preloadedState</code>. We will pass these to our template to get the final HTML page.</li>
</ol>
<h4 id="heading-2-srctemplatejs"><code>2. src/template.js</code></h4>
<p><code>template.js</code> exports a function. It takes <code>title</code>, <code>state</code> and <code>content</code> as input. It injects them into the template and returns the final HTML document.</p>
<p>To pass along the state, the template attaches <code>state</code> to <code>window.__STATE__</code> inside a <code>&lt;scri</code>pt&gt; tag.</p>
<p>Now you can read <code>state</code> on the client side by accessing <code>window.__STATE__</code>.</p>
<p>We also include the SSR companion <code>assets/client.js</code> client-side application in another script tag.</p>
<p>If you request the pure client version, it only puts <code>assets/bundle.js</code> inside the script tag.</p>
<h3 id="heading-the-client-side">The Client Side</h3>
<p>The client side is pretty straightforward.</p>
<h3 id="heading-1-srcbundlejs">1. src/bundle.js</h3>
<p>This is how you write the React and Redux <code>Provider</code> wrap. It is our pure client-side app. No tricks here.</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> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom'</span>;
<span class="hljs-keyword">import</span> { Provider } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-redux'</span>;
<span class="hljs-keyword">import</span> configureStore <span class="hljs-keyword">from</span> <span class="hljs-string">'./redux/configureStore'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/app'</span>;

<span class="hljs-keyword">const</span> store = configureStore();
render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span> &gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>,
  <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#app'</span>)
);
</code></pre>
<h3 id="heading-2-srcclientjs">2. src/client.js</h3>
<p>Looks familiar? Yeah, there is nothing special except <code>window.__STATE__.</code> All we need to do is grab the initial state from <code>window.__STATE__</code> and pass it to our <code>configureStore()</code> function as the initial state.</p>
<p>Let’s take a look at our new client file:</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> { hydrate } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom'</span>;
<span class="hljs-keyword">import</span> { Provider } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-redux'</span>;
<span class="hljs-keyword">import</span> configureStore <span class="hljs-keyword">from</span> <span class="hljs-string">'./redux/configureStore'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/app'</span>;

<span class="hljs-keyword">const</span> state = <span class="hljs-built_in">window</span>.__STATE__;
<span class="hljs-keyword">delete</span> <span class="hljs-built_in">window</span>.__STATE__;
<span class="hljs-keyword">const</span> store = configureStore(state);
hydrate(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span> &gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>,
  <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#app'</span>)
);
</code></pre>
<p>Let’s review the changes:</p>
<ol>
<li>Replace <code>render()</code> with <code>hydrate()</code>. <code>[hydrate()](https://reactjs.org/docs/react-dom.html#hydrate)</code> is the same as <code>render()</code> but is used to hydrate elements rendered by <code>[ReactDOMServer](https://reactjs.org/docs/react-dom-server.html)</code>. It ensures that the content is the same on the server and the client.</li>
<li>Read the state from the global window object <code>window.__STATE__</code>. Store it in a variable and delete the <code>window.__STATE__</code>.</li>
<li>Create a fresh store with <code>state</code> as initialState.</li>
</ol>
<p>All done here.</p>
<h2 id="heading-putting-it-all-together">Putting it all together</h2>
<h3 id="heading-indexjs">Index.js</h3>
<p>This is the entry point of our application. It handles requests and templating.</p>
<p>It also declares an <code>initialState</code> variable. I have modelled it with data in the <code>assets/data.json</code> file. We will pass it to our <code>ssr()</code> function.</p>
<p><em>Note: While referencing a file that is inside <code>src/</code> from a file outside <code>src/</code> , use normal <code>require()</code> and replace <code>src/</code> by <code>views/</code>. You know the reason (Babel compile).</em></p>
<p>Routing</p>
<ol>
<li><code>/</code>: By default server-rendered homepage.</li>
<li><code>/client</code>: Pure client-side rendering example.</li>
<li><code>/exit</code>: Server stop button. Only available in development.</li>
</ol>
<h4 id="heading-build-amp-run">Build &amp; Run</h4>
<p>It’s time to build and run our application. We can do this with a single line of code.</p>
<pre><code class="lang-bash">npm run build &amp;&amp; npm run start
</code></pre>
<p>Now, the application is running at <a target="_blank" href="http://localhost:3000/">http://localhost:3000</a>.</p>
<h3 id="heading-ready-to-become-a-react-pro">Ready to become a React Pro?</h3>
<p>I am starting a new series from next Monday to get your React skills blazing, immediately.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*TEecv1nLg253xmyGgddhOw.gif" alt="Image" width="500" height="226" loading="lazy">
<em>subscription link below ?</em></p>
<h3 id="heading-thank-you-for-reading-this">Thank you for reading this.</h3>
<p>If you like it and find it useful, follow me on <a target="_blank" href="http://twitter.com/rohitkrops">Twitter</a> &amp; <a target="_blank" href="http://bit.ly/2zVj1fX">Webflow</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add to Homescreen in a Progressive Web App ]]>
                </title>
                <description>
                    <![CDATA[ Add To Homescreen Here the web app install banner is focused on web app, with the feature of add to homescreen. Browser Support for Add To Homescreen Add to Homescreen functionality is currently supported by: Chrome iOS Safari You can see the lates... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-to-homescreen-in-a-progressive-web-app/</link>
                <guid isPermaLink="false">66c34ee85ced6d98e4bd332d</guid>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                    <category>
                        <![CDATA[ toothbrush ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 01 Feb 2020 00:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/02/daniel-korpai-beHREvNpn6k-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-add-to-homescreen"><strong>Add To Homescreen</strong></h2>
<p>Here the web app install banner is focused on web app, with the feature of add to homescreen.</p>
<h3 id="heading-browser-support-for-add-to-homescreen"><strong>Browser Support for Add To Homescreen</strong></h3>
<p>Add to Homescreen functionality is currently supported by:</p>
<ul>
<li>Chrome</li>
<li>iOS Safari</li>
</ul>
<p>You can see the latest status of browser support of this feature <a target="_blank" href="https://caniuse.com/#feat=web-app-manifest">here</a>.</p>
<h3 id="heading-on-android"><strong>On Android</strong></h3>
<p>On Android, the “add to homescreen” banner comes up automatically if you meet certain requirements. This is what it should look like on Android:</p>
<p>Add to homescreen promptWhen app launched</p>
<p><img src="https://user-images.githubusercontent.com/15358452/31663686-860779f0-b375-11e7-85c9-1387d9b3bfcf.png" alt="prompt" width="1080" height="1920" loading="lazy"></p>
<p><img src="https://user-images.githubusercontent.com/15358452/31663690-89b0d998-b375-11e7-8a84-f3e33be9a2c2.png" alt="launch" width="1080" height="1920" loading="lazy"></p>
<h3 id="heading-requirements">Requirements</h3>
<p>include a <code>manifest.json</code> file with the following properties:</p>
<ul>
<li><code>short name</code></li>
<li><code>name</code></li>
<li><code>192x192</code> size of <code>png</code> icon</li>
<li><code>start_url</code></li>
<li>include a service worker that is both registered and activated</li>
<li>the website served over HTTPS (you can still try this with localhost without HTTPS)</li>
<li>the website meets engagement heuristics defined by Chrome</li>
</ul>
<h4 id="heading-manifestjson"><strong>manifest.json</strong></h4>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"FreeCodeCamp"</span>,
  <span class="hljs-attr">"short_name"</span>: <span class="hljs-string">"FreeCodeCamp"</span>,
  <span class="hljs-attr">"theme_color"</span>: <span class="hljs-string">"#00FF00"</span>,
  <span class="hljs-attr">"background_color"</span>: <span class="hljs-string">"#00FF00"</span>,
  <span class="hljs-attr">"display"</span>: <span class="hljs-string">"standalone"</span>,
  <span class="hljs-attr">"Scope"</span>: <span class="hljs-string">"/"</span>,
  <span class="hljs-attr">"start_url"</span>: <span class="hljs-string">"/"</span>,
  <span class="hljs-attr">"icons"</span>: [
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-72x72.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"72x72"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-96x96.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"96x96"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-128x128.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"128x128"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-144x144.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"144x144"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-152x152.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"152x152"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-192x192.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"192x192"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-384x384.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"384x384"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    },
    {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"assets/images/icons/icon-512x512.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"512x512"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    }
  ],
  <span class="hljs-attr">"splash_pages"</span>: <span class="hljs-literal">null</span>
}
</code></pre>
<ul>
<li><code>name</code> is the name of the web app. (It will be shown in the launch screen)</li>
<li><code>short name</code> is the short name of the web app. (It will be shown below the icon of phone menu)</li>
<li><code>theme_color</code> is the color of the top of the browser.</li>
<li><code>background_color</code> is the background color of the launch screen.</li>
<li><code>display</code> is the way the web app should display once launched on the phone.</li>
<li><code>start_url</code> define the starting url of the website.</li>
<li><code>icons</code> is an array that store all the images location, sizes and type.</li>
</ul>
<h2 id="heading-on-other-devices">On other devices</h2>
<p>Although this method does not work on iOS and Windows, there is a fallback method.</p>
<h3 id="heading-ios">iOS</h3>
<p>On iOS, the “add to homescreen” button must be added manually. Add the following meta tags to the head section of your HTML to support iOS web app icon.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"apple-mobile-web-app-capable"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"yes"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"apple-mobile-web-app-status-bar-style"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"green"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"apple-mobile-web-app-title"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"FreeCodeCamp"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-72x72.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"72x72"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-96x96.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"96x96"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-128x128.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"128x128"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-144x144.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"144x144"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-152x152.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"152x152"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-192x192.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"192x192"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-384x384.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"384x384"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/assets/images/icons/icon-512x512.png"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"512x512"</span>&gt;</span>
</code></pre>
<h3 id="heading-windows">Windows</h3>
<p>On windows phone, add the following meta tags to the head section of your HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"msapplication-TileImage"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"/assets/images/icons/icon-144x144.png"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"msapplication-TileColor"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"green"</span>&gt;</span>
</code></pre>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Do you really need a PWA? Here are four questions to help you decide. ]]>
                </title>
                <description>
                    <![CDATA[ Why you need a PWA is not in question. Let’s see why you may NOT need it My inbox has been filled with questions regarding PWAs after my last two articles.  My “Practical Tips on Progressive Web App Development” shared the actual advantages of the PW... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/four-questions-to-understand-if-you-need-pwa/</link>
                <guid isPermaLink="false">66be14b49f6cdc38e7dec50d</guid>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oleh Romanyuk ]]>
                </dc:creator>
                <pubDate>Thu, 26 Sep 2019 14:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/Do-you-need-PWA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-why-you-need-a-pwa-is-not-in-question-lets-see-why-you-may-not-need-it">Why you need a PWA is not in question. Let’s see why you may NOT need it</h2>
<p>My inbox has been filled with questions regarding PWAs after my last two articles. </p>
<p>My “<a target="_blank" href="https://www.freecodecamp.org/news/practical-tips-on-progressive-web-app-development/">Practical Tips on Progressive Web App Development</a>” shared the actual advantages of the PWA technology, and my “<a target="_blank" href="https://keenethics.com/blog/progressive-web-apps-vs-native-which-to-choose-and-when">Progressive Web Apps vs Native</a>” further strengthened many people's belief that a progressive web app is exactly what they need.</p>
<p>I am exceptionally happy that I managed to convince them that PWAs are the future of web apps. I support the PWA technology as a path that the software development industry should take in the future. But progressive web apps are not a panacea, not a one-size-fits-all solution.</p>
<p>Since I have already explained <em>what PWA means</em> and <em>why you need</em> a <em>PWA</em>, today, I am going to finish the job and show <em>why you may NOT need</em> a <em>PWA</em>.</p>
<p>Making the choice between using and not using PWA is easier than you assume. Just answer four simple questions and you will see the right path yourself.</p>
<h2 id="heading-1-do-you-have-any-ready-made-developments">1. Do you have any ready-made developments?</h2>
<p>The speed and simplicity of the progressive web app development have been mentioned among the major benefits of PWAs. However, this advantage can be negated by the fact that you already have some ready-made developments for a mobile app. </p>
<p>Converting a ready-made mobile app to a progressive web app cannot be done with a few simple clicks. Instead, a progressive web app has to be developed from scratch. If you had already gone through the lengthy and complicated process of native app development, why would you throw it out and start everything from the beginning?</p>
<p>Surely, your mobile app can be outdated or inefficient. In this case, you have to choose whether to revive it with the help of a mobile app development company or to build an entirely new application using the PWA technology.</p>
<p>For instance, <a target="_blank" href="https://www.pinterest.ca/">Pinterest</a> – one of the brightest examples of companies that implemented PWA – decided to choose both options. They have developed a progressive web app but did not forget about the traditional mobile application either.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/Progressive-web-app.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em><strong>Key idea</strong>: The PWA approach is a great solution if you have no ready-made developments. If you already have a native or cross-platform mobile version of your app, it will be easier and more productive to finish those rather than build a new application from scratch.</em></p>
<h2 id="heading-2-do-you-plan-to-focus-on-mobile-devices-only">2. Do you plan to focus on mobile devices only?</h2>
<p>The idea of PWA features is to improve the experience of a user accessing a web page from a mobile device. Until the PWA technology was introduced, the businesses willing to target both web and mobile had two options:</p>
<ul>
<li>to adapt a web page to mobile devices</li>
<li>to develop a mobile app from scratch</li>
</ul>
<p>The first option is clumsy and inconvenient from the perspective of a user. The second option is resource-consuming from the perspective of a business. </p>
<p>The advancement of PWAs brought the Golden Mean: convenience for the user and efficiency for the business. If you do not plan to enter the web or desktop niche but choose to focus solely on mobile apps, there is no need to build a PWA. </p>
<p>Surely, when developing progressive web apps, you win because you're developing one application for both Android and iOS. However, this is exactly what you can achieve with cross-platform development with Ionic, Cordova, or React Native.  </p>
<p>Meanwhile, Pinterest has developed a progressive web app to augment their already existent web platform and mobile app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/Progressive-web-app--4-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em><strong>Key idea</strong>: PWAs work best for the businesses targeting both web and mobile platforms. If you choose to focus on only one of those, there is no reason to choose progressive web app development.</em></p>
<h2 id="heading-3-are-you-reluctant-to-let-your-users-access-the-app-via-a-web-browser">3. Are you reluctant to let your users access the app via a web browser?</h2>
<p>Another essential difference between mobile apps and progressive web apps rests in the fact that the former are downloaded from the store, while the latter – via a shared link or directly from a website as it is illustrated below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/Progressive-web-app--3-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Tastes differ. If you think that working with App Store or Google Play is easier than fighting the SEO competition on Google or inspiring users to share your links, you should feel free to choose mobile app development. </p>
<p>If you think that it is more difficult to be featured in top search results in Stores comparing to Google search results, go with PWA. Besides, if your website already has a sufficiently large target audience of devoted users, progressive web apps would work best for you.</p>
<hr>
<p><em><strong>Key idea</strong>: If you want your users to access the application via the App Store or Google Play rather than through a browser, or if app store optimization (ASO) sounds simpler than search engine optimization (SEO), you should go with a mobile app.</em></p>
<h2 id="heading-4-do-you-need-advanced-mobile-device-features">4. Do you need advanced mobile device features?</h2>
<p>While PWAs can work with geolocation, a lot of advanced mobile device features remain beyond their reach. Progressive web apps do not work with proximity sensors, advanced camera controls, audio or video recording, fingerprint scanning, NFC, or even Bluetooth. </p>
<p>Ask your development team for the PWA checklist of features that they will not be able to implement. If you cannot do without at least one of these features, you should opt in favor of mobile development.</p>
<p>For instance, the progressive web app of Pinterest does not feature the function of taking photos, which is present in their mobile app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/Progressive-web-app--2--1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em><strong>Key idea</strong>: Incompatibility with native device features can hinder you from developing a PWA. While this issue may be solved in the future, for now, you should go with mobile apps instead.</em></p>
<h2 id="heading-to-wrap-up">To Wrap Up</h2>
<p>Before asking for a PWA, you need to decide whether you really need the PWA approach. Start by answering four simple questions:</p>
<ol>
<li>Do you have any ready-made developments, such as a native mobile app?</li>
<li>Do you want to focus on mobile devices only? No desktop or web platforms?</li>
<li>Are you reluctant to let your users access the app via a web browser?</li>
<li>Do you need advanced mobile device features? Are you going to use fingerprint scanning, NFC, or Bluetooth?</li>
</ol>
<p>If you answer "yes" to the majority of these questions about progressive web apps, then, probably, you do not need a progressive web app. A mobile app will fully cover all your needs.</p>
<h2 id="heading-do-you-have-an-idea-for-a-pwa"><strong>Do you have an idea for a PWA?</strong></h2>
<p>My company KeenEthics is an experienced <a target="_blank" href="https://keenethics.com/tech-apps-progressive-web-apps">progressive web app development company</a>. In case you need the following services, feel free to <a target="_blank" href="https://keenethics.com/contacts">get in touch</a><em>.</em></p>
<p>You can read more of my articles on the KeenEthics blog: <a target="_blank" href="https://keenethics.com/blog/four-questions-to-understand-if-you-need-pwa">Four Questions to Understand If You Need PWA</a>.</p>
<h2 id="heading-ps"><strong>P.S.</strong></h2>
<p>Also, I would like to say "thank you" to <a target="_blank" href="https://www.linkedin.com/in/tania-matviiok-222b9b16a/">Tetiana Matviiok</a> for helping me edit this article.</p>
<p>The original article posted on KeenEthics blog can be found here: <a target="_blank" href="https://keenethics.com/blog/progressive-web-apps-vs-native-which-to-choose-and-when">Progressive Web Apps vs Native: Which to Choose and When?</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ An overview of Progressive Web App development ]]>
                </title>
                <description>
                    <![CDATA[ Ordinary mobile apps do not seem to work anymore. The number of apps in the App Store and Play Market is overwhelmingly huge. This is a challenge both for businesses, who want their apps to be visible, and for customers, who try to find the applicati... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/practical-tips-on-progressive-web-app-development/</link>
                <guid isPermaLink="false">66be14cb2969640c348b5691</guid>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oleh Romanyuk ]]>
                </dc:creator>
                <pubDate>Thu, 15 Aug 2019 15:10:35 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9ca0d7740569d1a4ca4b1b.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Ordinary mobile apps do not seem to work anymore. The number of apps in the App Store and Play Market is overwhelmingly huge. This is a challenge both for businesses, who want their apps to be visible, and for customers, who try to find the application they need. </p>
<p>And the development of traditional mobile apps is costly and time-consuming, while the user experience is slow and tangled. </p>
<p>To solve these issues and to provide optimal business opportunities and user experience, Progressive Web Apps enter the game. What is PWA technology? Why does progressive web application development constitute the future? What does a progressive web app development company do? We will answer all of these questions in this article.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/PWA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-what-are-progressive-web-applications">What Are Progressive Web Applications?</h2>
<p>In the simplest terms, a progressive web app is a website made to resemble an application installed on your smartphone, laptop, tablet, or desktop.</p>
<p>PWA development is a set of optimal software development practices aimed at making a web application function similarly to a mobile or desktop app. Similarly to a mobile application, PWAs send push notifications and have an icon on the home screen. At the same time, progressive web applications are simpler and faster than traditional mobile apps, and they can be shared through a URL. </p>
<p>Progressive web app developers optimize web applications since they provide app-like navigation and excellent visual content. This is implemented with the help of javascript files, called service workers, which enable offline execution of the app and offline information storage.</p>
<p>PWA development helps the developers and businesses bridge the gap between web and native experiences, which sometimes results in time-consuming, discouraging, or unreliable user experience. </p>
<p>A progressive web app development company understands that having to look for an app in the store is too much effort for modern-day users. People strive to save their time and effort and, at the same time, to find the most effective solutions. Therefore, PWA technology is applied to enable users to access a native-like mobile version of their favorite website with a single tap.</p>
<p>Progressive web app development companies employ the most recent web technology aimed at delivering the most amazing web experience to the user. By boosting the performance, accessibility, and user engagement, progressive web app developers ensure that these apps are fast, engaging, and reliable.</p>
<h2 id="heading-what-are-the-benefits-of-progressive-web-applications">What Are the Benefits of Progressive Web Applications?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Buying-a-Ready-Made--2--min.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol>
<li><strong>Speed of lightning:</strong> A progressive web app development company makes the application load faster than a regular website, which is pivotal for an excellent user experience.</li>
<li><strong>Simplicity and perfection:</strong> A progressive web application may be installed easily and quickly with a few taps without leaving the webpage or opening the store.</li>
<li><strong>Universality:</strong> Progressive web apps are compatible with all devices and platforms, namely Windows, MacOS, Linux, and Chrome OS.</li>
<li><strong>Personal touch:</strong> PWA developers make sure that their apps are more personalized than regular mobile apps, which greatly boosts customer engagement and loyalty.</li>
<li><strong>Striking balance:</strong> PWAs help to increase organic traffic and to decrease bounce rate.</li>
<li><strong>No more Downasaur:</strong> By using cached data from the earlier online activities, progressive web app development company makes their applications able to perfectly work without access to the Internet.</li>
</ol>
<blockquote>
<p><em>The greatest benefit of a progressive web app is the ability to work offline and the absence of all the bars and buttons that a mobile browser has.</em> </p>
<p>_Se_rgey Gornostaev, Full-Stack Developer</p>
</blockquote>
<p>Not only progressive web applications are more convenient for the user, but also they are cheaper and faster to develop for the business. In fact, PWA developers have no need to write separate codes for different platforms, which greatly limits the overall cost of the final product and the time spent on its development. Also, since PWAs are installed from the website itself, developers do not need to mess with deploying the app to the app store, following all the requirements on design or logic of the app, improving the visibility of the app, etc. Such an app is offered only on the website itself, which spares users the feeling that it is a one-time install-click-and-delete app from the store.</p>
<h2 id="heading-what-are-the-drawbacks-of-progressive-web-applications">What Are the Drawbacks of Progressive Web Applications?</h2>
<ol>
<li><strong>Decentralized search:</strong> A user cannot just enter the app store and find the necessary app or stumble upon a random one. Either users know what they want or they are not likely to find it.</li>
<li><strong>Bad news for Apple Lovers:</strong> While Microsoft and Google work to develop and encourage the development of PWAs, Apple does not seem very eager to play by their rules. Even though Safari already supports PWAs, the functionality is limited, and the users are unable to experience all the benefits of this technology.</li>
<li><strong>Isolation:</strong> Different PWAs cannot share resources or data among themselves because they are highly separated.</li>
</ol>
<h2 id="heading-why-is-pwa-development-the-future">Why Is PWA Development the Future?</h2>
<p>Progressive app development is strongly patronized by Google Inc., which believes PWA development to be the future of software. According to Google articles, with progressive web apps, <a target="_blank" href="https://developers.google.com/web/showcase/2016/aliexpress">AliExpress</a> has boosted the conversion rate for its new users by 104% and increased average time spent per session by about 74%. </p>
<p>PWA developers helped a Saudi retailer of electronics <a target="_blank" href="https://developers.google.com/web/showcase/2016/extra">eXtra Electronics</a>  generate 100% more sales and to attract new users via web push notifications, which are also the part of a progressive web app. </p>
<p>Similarly, an American retailer <a target="_blank" href="https://developers.google.com/web/showcase/2016/5miles">5miles</a> has increased conversions by 60% and decreased bounce rate by 50%. Also, with the help of a PWA development company, a Nigerian e-commerce platform Konga has cut data usage by about 92%. A progressive web app is also actively used by <a target="_blank" href="https://developer.washingtonpost.com/pb/blog/post/2016/07/15/amp-up-with-progressive-web-apps/"><em>The Washington Post</em></a><em>,</em> and it has enabled them to decrease page loading time by ten times.</p>
<p>Today, PWAs are already actively used by such user-favorite social networking platforms as Twitter, <a target="_blank" href="https://appsco.pe/app/instagram">Instagram</a>, <a target="_blank" href="https://appsco.pe/app/telegram">Telegram</a>, <a target="_blank" href="https://appsco.pe/app/pinterest">Pinterest</a>, and <a target="_blank" href="https://appsco.pe/app/tinder">Tinder</a>. <a target="_blank" href="https://threejs.org/editor/">Three.js</a> – a popular 3D model editor – is also available as a PWA. Taxi services <a target="_blank" href="https://appsco.pe/app/uber">Uber</a> and <a target="_blank" href="https://appsco.pe/app/lyft">Lyft</a> also offer PWAs for their customers. </p>
<p>Progressive web apps can also be used to make games more enjoyable as in the case of <a target="_blank" href="https://appsco.pe/app/2048">2048</a> or <a target="_blank" href="https://appsco.pe/app/breaklock">BreakLock</a>. PWAs are also employed by such a well-known entertainment platform as <a target="_blank" href="https://appsco.pe/app/9gag">9GAG</a>, a coffee retailer <a target="_blank" href="https://appsco.pe/app/starbucks">Starbucks</a>, or a popular service <a target="_blank" href="https://appsco.pe/app/googlemaps">Google Maps</a>.</p>
<p>PWAs are equally loved by Google and Microsoft as both companies recognize great potential in this technology. It is rumored, however, that Microsoft has immense hopes for progressive web apps as it plans to reenter the smartphone market with its innovative Andromeda device. While the launch of this foldable smartphone is scheduled for late 2019, it is generally believed that Microsoft will bet not only on the revolutionary hardware solution but also on all-PWA software for the updated OS.</p>
<p>And yet, the niche is still almost free, and as of March 2019, there were only 32 PWAs published on Windows 10 Store. To compare, at the end of 2018, this very store featured about 700,000 regular apps. More than that, as of now, there are no PWAs for smart TVs or any other IoT devices, which yet could greatly benefit from such a technology. Therefore, progressive web apps are a perfect choice for those who want to make their business unique, to impress the customer, and to pioneer in such a promising field.</p>
<blockquote>
<p><em>As long as mobile devices are gradually overtaking desktops, PWA is a perfect opportunity for a business to broaden its circle of influence and to take a firm position online. At the same time, one can greatly save on the development and maintenance of the app.</em></p>
<p>Anton Morya, Full-Stack Software Developer</p>
</blockquote>
<h2 id="heading-what-does-a-progressive-web-app-development-company-do">What Does a Progressive Web App Development Company Do?</h2>
<p>As in the case of mobile or web software, progressive web app developers start with the discovery stage, prototyping, POC, or <a target="_blank" href="https://keenethics.com/approach-minimum-viable-product">MVP</a>. After that, the PWA team should create a responsive design, which will look perfect at any platform and any device. The progressive app development process itself should be tailored to the needs of a specific business, and PWA developers should be fully aware of customer needs and interests. The product has to be continuously tested by a progressive web app development company in order to fix mistakes and prevent further errors from occurring.</p>
<p>If the business already has a website and wants to adapt it to the PWA technology, the progressive web app development company should provide a well-detailed and properly planned migration strategy.</p>
<p>A PWA development company may also offer SEO services to make sure that the website and, respectively, the progressive web application are easily searchable. An SEO-friendly PWA can considerably boost web-traffic and conversion rates.</p>
<p>After the progressive web application development is over and the final product is launched, a reliable PWA development company offers its customers a team of PWA developers for continuous support and maintenance.</p>
<h2 id="heading-do-you-have-an-idea-for-a-pwa">Do you have an idea for a PWA?</h2>
<p>My company KeenEthics is an experienced <a target="_blank" href="https://keenethics.com/tech-apps-progressive-web-apps">progressive web app development company</a>. In case you need the following services, feel free to <a target="_blank" href="https://keenethics.com/contacts">get in touch</a><em>.</em></p>
<p>The original article posted on KeenEthics blog can be found here: <a target="_blank" href="https://keenethics.com/blog/practical-tips-on-progressive-web-app-development">Practical Tips on Progressive Web App Development</a>.</p>
<h2 id="heading-ps">P.S.</h2>
<p>I would like to say thank you to everyone who contributed to this article including Sergey Gornostaev and Anton Moriia, full-stack software developers @ Keenethics, as well as Tetiana Matviiok, content manager @ KeenEthics.</p>
<p>The original article posted on KeenEthics blog can be found here: <a target="_blank" href="https://keenethics.com/blog/practical-tips-on-progressive-web-app-development">Practical Tips on Progressive Web App Development</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to get your team on board with accessibility ]]>
                </title>
                <description>
                    <![CDATA[ By James Y Rauhut We all learn about web accessibility at different points in our career. That means a lot of time you are not on the same page as your teammates. I had the privilege a couple of months ago to speak at Pingboard about accessibility. O... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-align-your-team-on-the-need-for-accessibility/</link>
                <guid isPermaLink="false">66d460f451f567b42d9f84a7</guid>
                
                    <category>
                        <![CDATA[ a11y ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ presentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ teamwork ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 13 Aug 2019 21:40:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/keyboard-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By James Y Rauhut</p>
<p>We all learn about web accessibility at different points in our career. That means a lot of time you are not on the same page as your teammates. I had the privilege a couple of months ago to speak at <a target="_blank" href="http://pingboard.com/">Pingboard</a> about accessibility. Our goal was to get the whole team at the same knowledge starting point. If we all have a basic understanding of whom web accessibility affects and how it affects them, we can ship better experiences.</p>
<p>You probably find yourself in the same opportunity at your company to present on accessibility. So I would like to do two things to help: I am going to <a target="_blank" href="https://drive.google.com/file/d/1W62aya8uk0LgMPyMUBSIAJVOQBewmiKd/view?usp=sharing">give you my presentation</a> as a starting point and walk you through the points I like to touch on.</p>
<blockquote>
<p>You probably find yourself in the same opportunity at your company to present on accessibility. …I am going to <a target="_blank" href="https://drive.google.com/file/d/1W62aya8uk0LgMPyMUBSIAJVOQBewmiKd/view?usp=sharing">give you my presentation</a> as a starting point and walk you through the points I like to touch on.</p>
</blockquote>
<h1 id="heading-remind-the-team-that-you-are-talking-about-real-people"><strong>Remind the team that you are talking about real people.</strong></h1>
<p>When we read accessibility documentation, it is easy to forget the human element. It makes sense because you are reading technical docs meant to influence code. It is great to start with this shared definition:</p>
<p><strong><em>A person with a disability:</em></strong> <em>A person who has a physical or mental impairment that substantially limits one or more major life activity.</em></p>
<p>We use this to establish friendly dialog. People do not want to be called “disabled”. They want to be called their name. We also need to clarify how wide of a range disabilities can be. Try expanding past assumptions early on with these points:</p>
<ul>
<li>Some disabilities come at birth, some come later.</li>
<li>Some disabilities are permanent, some are temporary.</li>
<li>Some disabilities always affect, some come and go.</li>
<li>Some disabilities are visible, some are invisible.</li>
</ul>
<h1 id="heading-go-over-some-disability-categories-with-emotional-experiences-and-quick-tips"><strong>Go over some disability categories with emotional experiences and quick tips.</strong></h1>
<p>Now that we have established that we are talking about people, it is time to talk about their experiences. I like to mix this section with quick tips for common disability categories. Remind your audience that there are way more disabilities than you are covering. They are difficult to categorize, which is why the technical documentation focuses on the solutions.</p>
<p>Something you will notice about the presentation is that there is a lot of video and audio. I find it more effective to have those with disabilities speak more than me about the issue. The multimedia in the presentation makes it possible for those people to not even have to be there.</p>
<h3 id="heading-visual">Visual</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/UzffnbBex6c" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>I love to share this video of Tommy Edison using a screenreader because he keeps things lighthearted, but also goes through the whole process of sending an email. After the video, you can point out that fellow Mac users can try their screenreader with <code>CMD + F5</code> at anytime.</p>
<p>Quick tips:</p>
<ul>
<li>People with dyslexia prefer to override font settings.</li>
<li>People with low vision need to be able to zoom correctly.</li>
<li>People with color blindness need an overall color contrast ratio of 4.5:1. Text 19px or larger can have a ratio of 3:1.</li>
<li>People with color blindness need labels and patterns for differentiations.</li>
</ul>
<h3 id="heading-auditory-and-seizure">Auditory and Seizure</h3>
<p>Auditory disabilities are easier to talk about with digital product teams. Remind your team that all audio should be paired with visual cues and captions. Encourage the team to do content audits to check all videos for closed captioning.</p>
<p>Strobing, flickering, and flashing can trigger seizures. Other triggers include animations longer than 250ms, parallax, and images moving under text.</p>
<h3 id="heading-motor">Motor</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/yx7hdQqf8lE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>There are two demos I like to show teammates when it comes to motor disabilities. The first is hidden inside a longer video. A fellow named Gordin Richins shows what it is like to use a mouth stick. It is an older video, but I try to point out that new technologies can be more expensive.</p>
<p>The second video is a wholesome video of an eye tracking product. These are great because they can provide mouse capabilities to those with motor disabilities. However, we should still make all experiences keyboard accessible to be safe.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/FEQv7buTNxw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-cognitive">Cognitive</h3>
<p>Cognitive disabilities can be difficult to convey. For this last category, I stuck with quick tips to keep the presentation alternative between facts and emotion. Here are the quick tips I share:</p>
<ul>
<li>For memory, keep processes short and remind users of context as much as possible.</li>
<li>For problem-solving, error messages should be as explanatory as possible.</li>
<li>For attention, use visual cues to highlight the most important points or sections of content.</li>
<li>For reading, linguistic, and verbal comprehension, provide supplemental media that helps processes.</li>
</ul>
<h1 id="heading-emphasize-how-common-disabilities-actually-are"><strong>Emphasize how common disabilities actually are.</strong></h1>
<p>Did you know that one out of five people in the US have at least a one disability? (<a target="_blank" href="https://www.census.gov/newsroom/releases/archives/miscellaneous/cb12-134.html">source</a>) It may not seem like that in the workplace, but we should consider why. This is the point of the presentation when people should understand invisible disabilities. Invisible disabilities can be hidden from the naked eye. Here is a great interview where Carly Medosch talks about working with an invisible disability:</p>
<p><a target="_blank" href="https://www.npr.org/2015/03/08/391517412/people-with-invisible-disabilities-fight-for-understanding"><strong>NPR: People with 'Invisible Disabilities' Fight for Understanding</strong></a></p>
<p>This story is a great transition to a big question: What can we as enterprise software teams do to help those with disabilities?</p>
<p>Well, 79% of people of a working age in the US have employment. Only 41% of people of a working age in the US that have disabilities have employment. (<a target="_blank" href="https://www.census.gov/newsroom/releases/archives/miscellaneous/cb12-134.html">source</a>) If more jobs were accessible, that gap would close. This means that we as enterprise software teams can make it our mission to close the gap!</p>
<h1 id="heading-end-with-the-legal-risk-for-those-that-need-extrinsic-motivation"><strong>End with the legal risk for those that need extrinsic motivation.</strong></h1>
<p>It may not feel great, but some people may still need more reasoning about why the team should work towards accessible experiences. This is why I like to close the presentation on the legal implications of accessibility.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-13-at-3.45.32-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>ADA Title 3 lawsuits in federal court: 2722 in 2013, 4436 in 2014, 4789 in 2015, 6601 in 2016, 7663 in 2017, 10163 in 2018. https://www.adatitleiii.com/2019/01/number-of-ada-title-iii-lawsuits-filed-in-2018-tops-10000/</em></p>
<p>In 1990, the Americans with Disabilities Act was signed. This provides those with disabilities the same protection that’s given in the Civil Rights Act of 1964. Section 508 says digital experiences in government departments and agencies have accessibility requirements.</p>
<p>Lawsuits continue to grow saying that the ADA also covers digital experiences from any company. Over 10,000 lawsuits were filed in 2018 alone. In fact, one of those <a target="_blank" href="https://www.cnbc.com/2019/07/25/dominos-asks-supreme-court-to-say-disability-protections-dont-apply-online.html">cases is headed towards supreme court</a>.</p>
<h1 id="heading-be-a-part-of-the-good-fight">Be a part of the good fight.</h1>
<p>Are you considering presenting to your team on web accessibility? You really should. You don’t have to be an expert and it’s okay if not everyone listens to you. Every effort to make the web a more friendly place is worth it.</p>
<p>I hope these resources helped you shape a future presentation. Please steal everything from me (but keep the citations).</p>
<p>If you appreciate this, please consider voting for my SXSW talk idea. I want to teach product managers, designers, and everyone else about progressive web apps. The cool thing about PWAs, is that a lot of accessibility criteria is baked in! If you wanna learn more, check out the video below.</p>
<p><a target="_blank" href="https://panelpicker.sxsw.com/vote/95517">Please take minute to vote for my talk and share it with others.</a></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/aRwfB7Iiaqo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Got any other good resources for accessibility presentations? Please share them in the comments or tweet me them at <a target="_blank" href="http://twitter.com/seejamescode">@seejamescode</a>. I will retweet the best ones!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to design and build a carousel feature in VueJS ]]>
                </title>
                <description>
                    <![CDATA[ By Fabian Hinsenkamp A carousel, slideshow, or slider — however you call it this class of UI — has become one of the core elements used in modern web development. Today, it’s almost impossible to find any Website or UI library which doesn’t come with... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-design-and-build-a-carousel-feature-in-vuejs-125f690a3a9e/</link>
                <guid isPermaLink="false">66c351a0180d77b39dd650a2</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 28 Feb 2019 18:04:11 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*DbM53D4CMawSuTN7V9AEqg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabian Hinsenkamp</p>
<p>A carousel, slideshow, or slider — however you call it this class of UI — has become one of the core elements used in modern web development. Today, it’s almost impossible to find any Website or UI library which doesn’t come with one or another kind of carousel.</p>
<p>Why, though? In fact, carousels really deserve their popularity. They allow users to skim through available content without vertical scrolling or heavy mouse movements. Consequently, users save time and can focus on the displayed content as Carousels keep cognitive load to a minimum.</p>
<p><strong>This is reason enough to learn how to build a Carousel in VueJS!</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fWQBrTW-CQ3r47e9HtGrRg.gif" alt="Image" width="928" height="1128" loading="lazy"></p>
<p>All my tutorials gravitate around Progressive Vue Apps. This one is no exception! Making progressive apps means delivering a UX for mobile users close to native apps, including excellent performance, native features like push notifications, offline experience and much more. In a world where the majority of users experience the Web via mobile devices, there is no excuse left to not build progressive apps!</p>
<p>Of course you can still use the Carousel in any Vue app. You also don’t need any prior experience with VueJS or Progressive Web Apps for this tutorial!</p>
<p>You find the full code here:</p>
<p><a target="_blank" href="https://github.com/fh48/vue-pwa-carousel">https://github.com/fh48/vue-pwa-carousel</a></p>
<h3 id="heading-whats-our-vision-for-the-carousel">What’s our Vision for the Carousel?</h3>
<p>First thing we will do is to get an overview over what kind of components we want to build.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Sq_XDCNnG63BCNWP_zojnA.png" alt="Image" width="443" height="500" loading="lazy"></p>
<p>There are a few very straightforward ones:</p>
<ul>
<li><strong>Card</strong> → it holds the information of each carousel element.</li>
<li><strong>Carousel →</strong> parent which holds all logic</li>
<li><strong>ArrowButton</strong> → helps to navigate between multiple cards.</li>
<li><strong>Indicator</strong> → shows the number of cards and which is currently visible.</li>
</ul>
<h3 id="heading-initial-setup">Initial Setup</h3>
<p>If you want to learn how to set up a project, this section is for you. In case you do not, just continue with the next section.</p>
<pre><code>vue init pwa vue-pwa-carousel
</code></pre><p>We will be prompted to pick a preset — I recommend the following configuration:</p>
<pre><code>? Project name vue-pwa-carousel? Project short name: fewer than <span class="hljs-number">12</span> characters to not be truncated on homescreens (<span class="hljs-keyword">default</span>: same <span class="hljs-keyword">as</span> name) vue-pwa-carousel? Project description A simple tax calculator ? Author Fabian Hinsenkamp? Vue build runtime? Install vue-router? No? Use ESLint to lint your code? No? Pick an ESLint preset Standard? Setup unit tests <span class="hljs-keyword">with</span> Karma + Mocha? No? Setup e2e tests <span class="hljs-keyword">with</span> Nightwatch? No
</code></pre><p>Run <code>yarn</code> in the root folder of the project to install all dependencies. Now we have a project set-up which includes the basic technologies we need to build our carousel. Moreover, it already includes a Service Worker which pre-caches all our static files and allows users to access the app even when they are offline.</p>
<p>You can check out how the template app looks like by running <code>yarn start</code>.</p>
<p>To test how the carousel looks on mobile, we need to expose our development build to mobile devices through a public URL. There are many ways to do so, but here we use <code>ngrok</code> as it’s easy to set up and just does its job.</p>
<pre><code>yarn <span class="hljs-built_in">global</span> add ngrok
</code></pre><p>Next, we need to run our dev server and ngrok in two separate terminals.</p>
<h3 id="heading-lets-build-the-card">Let’s build the Card!</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*eR5j_AAOlOLRaTmNxm8hrQ.png" alt="Image" width="337" height="500" loading="lazy"></p>
<p>To save you some boring adjustments to the template app, just check out this branch<code>00_basicSetup</code>. It includes all data and styles we need to make this app meaningful and pretty.</p>
<p>The Card really does one thing: it shows the currently selected data. Which is, in our case, an <em>image</em>, a <em>headline</em> and some <em>text</em>.</p>
<p>Clearly, we do not want to build multiple cards with hard-coded content. Instead, we want to dynamically pass the data to the card and simply render it.</p>
<p>Based on that knowledge, we can now create our <code>Card.vue</code> file in the <code>src/components</code> folder. Moreover, we can already define the basic html structure and names and their types of the properties we want to pass to the card.</p>
<p>Attention: we store all icons we want to display locally in our assets folder. That means that our path remains the same, but we need to dynamically change the file names which are supposed to be rendered. Consequently, all properties are of type <code>String</code>.</p>
<p>Next, we make the <strong>Card</strong> render the headline and the related text. Therefore, we use the most basic way of data-binding within VueJS — the mustache tag.</p>
<p>It’s basically just curly braces around our prop variables. As soon as data comes in, <code>{{headline}}</code> and <code>{{text}}</code> will be replace with the related data object. This is always true, also when new data comes in, after is has rendered other data before.</p>
<p>Rendering the image dynamically is a bit more tricky. Remember, we do not pass the actual icon but only its file name.</p>
<p>So we basically want to consume the image as a module, like any other component. A static image we could consume by importing it in the script block and assign it to a variable. However, we do have changing path. As our app uses webpack there is a fantastic shorthand available to load these dynamically as follows:</p>
<pre><code>:src=<span class="hljs-string">"require(`@/assets/svg/${imgName}`)"</span>
</code></pre><p>The <code>:</code> syntax is the Vue-way to dynamically bind attributes to an expression. You might already have seen the <code>v-bind:</code> prefix which <code>:</code> is the shorthand for.</p>
<p>Now our <code>template</code> block is completed and looks as follows.</p>
<p>To finalise our <strong>Card</strong> component, we simply need to add the prepared styled to the bottom of the file.</p>
<pre><code>&lt;style src=<span class="hljs-string">"../assets/styles/Card.css"</span> scoped&gt;
</code></pre><p>Last thing we need to do for this section is to check if the <strong>Card</strong> actually works.</p>
<p>So let’s simply add it the our <code>App.vue</code> component. However, keep in mind that we will have to move the component into the <strong>Carousel</strong> component in the next section.</p>
<p>We add the following to the <code>&lt;templa</code>te&gt;<code>; and &amp;l</code>t;script&gt;<code>; block</code>s of App.vue.</p>
<p>What a fantastic result! Especially, since we can already dynamically change whatever the <strong>Card</strong> should display!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*X9YAXb78elPnqKN2da1SFQ.png" alt="Image" width="350" height="507" loading="lazy"></p>
<p>Next, we build the <strong>Carousel</strong> to have a dedicated component to manage all logic around showing different <strong>Cards</strong> based on user inputs.</p>
<p>Check out the branch <code>01_Card</code> to if you want to start from here or compare your implementation.</p>
<h3 id="heading-lets-build-the-carousel">Let’s build the Carousel</h3>
<p>The <strong>Carousel</strong> will be our reusable parent component. It will encapsulate all relevant components and logic.</p>
<p>Like with the Card before, we should focus on building the component in a way that it can handle a change in data gracefully. For example, it should be capable of handling varying numbers of cards being passed to it.</p>
<p>Next we will see how this approach translates into Code. First we start with creating a <strong>Carousel</strong> component and make the <strong>Card</strong> a child of <strong>Carousel</strong>.</p>
<p>The template block of the new component hosts the <strong>Card</strong>, wrapped in two wrapper elements. We will see later why these are necessary.</p>
<p>As we will pass data of multiple Cards to the Carousel, we need to specify that only the <code>currentElement</code> is rendered.</p>
<p>In the following <code>&lt;scri</code>pt&gt; block we need to define which of the passed Cards i<code>s the currentE</code>lement by default.</p>
<p>Therefore, we define the <code>currentElementIndex</code> initially to be <code>0</code>. VueJS comes with a feature that allows us to compute variables dynamically. We use it to select the data of the card that should render initially.</p>
<p>Now we only need to replace the <strong>Card</strong> with the <strong>Carousel</strong> in our <code>App.vue</code> file. To put a bit more structure and meaning to our final page, let’s wrap the carousel in another section element and place it before the other section.</p>
<p>That’s our basic implementation. It is not quite a Carousel yet but we are about to change this by adding the arrow buttons to switch between the objects we pass in the <code>cards</code> array to our <strong>Carousel</strong>.</p>
<p>Check out the <code>02_Carousel</code> to see the complete code for this section. If you coded along you should see the following in front of you.</p>
<h3 id="heading-lets-build-the-arrowbutton">Let’s build the ArrowButton</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*oT4qITnEHmeHR6hXhoM7dQ.png" alt="Image" width="500" height="259" loading="lazy"></p>
<p>Now we build the <strong>ArrowButton</strong> component, which receives its methods and the type of arrow icon from its parent. The implementation itself is strait forward.</p>
<p>The component is only responsible for rendering the correct styles and the icon. All logic related to the Buttons is added to the <strong>Carousel.</strong> That way, we have created a truly generic component we could use in any context where we want to use a button with an arrow icon.</p>
<p>Now, within <strong>Carousel,</strong> we add two methods to navigate between our card data objects. Methods are just another exported object within the <code>&lt;scri</code>pt&gt; block.</p>
<p>These simply increase or decrease the <code>currentElementIndex</code> by <code>1</code>. We use the index to compute the <code>currentElement</code> variable, so every time one of the methods is called the next card is displayed. We also add some restrictive conditions, as we want the Carousel not to loop.</p>
<p>Now we only need to add the <strong>ArrowButtons</strong> to basically complete our <strong>Carousel</strong>!</p>
<p>Here you see how we use the methods and computed values to implement one of our <strong>ArrowButtons</strong>. Try to implement the second one below the <strong>Card</strong> component.</p>
<p>In case you get stuck or something just looks wrong, checkout the <code>03_ArrowButton</code> branch. If everything worked out though, your carousel should look like the following:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fD7AdSRaWe7JRtW_2RNd5A.gif" alt="Image" width="400" height="504" loading="lazy"></p>
<h3 id="heading-lets-build-indicators">Let’s build Indicators!</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ILDy8sTuoLO9WdnpsKyACA.png" alt="Image" width="500" height="128" loading="lazy"></p>
<p>The last feature we’re going to add are the <strong>Indicators.</strong> These help users to understand how many Cards there are and which one they are currently looking at. Additionally, these allow the user to select individual Cards directly.</p>
<p>The component receives three properties. The array of <code>elements</code> we use to create a <code>&lt;</code>_l_i&gt; element for each card data object.</p>
<p><code>currentElementIndex</code> is required to add a CSS class that highlights the indicator related to the current card. Moreover, it is used to disable the button for the current card. That way we prevent it from being selectable is via the tab keys. That way, we provide at least a minimum of accessibility.</p>
<p><code>showElement</code> is the method which is called when a user clicks on an indicator. It’s passed from outside to keep the component as focused as possible. How an element is shown is clearly no concern of <strong>Indicators</strong>.</p>
<p>When we add the <strong>Indicators</strong> to <code>Carousel.vue</code> it becomes clear why we created two wrappers for the <strong>Card.</strong> Semantic and clear HTML is vital, especially for large projects with a high level of complexity.</p>
<p>You can check out the complete code at branch <code>04_Indicators</code>.</p>
<h3 id="heading-lets-add-swipe">Let’s add swipe</h3>
<p>Last but not least, we make our <strong>Carousel</strong> mobile friendly. A good progressive web app doesn’t start by caching static files, but with responsiveness.</p>
<p>As we do lack space on small screens, we hide the <strong>ArrowButtons</strong> and allow users to swipe in order to browse the <strong>Cards.</strong> Here the Indicators also do pay off again, as they function in mobile as the main indicator that users can swipe to see more cards.</p>
<p>Therefore we add the following library:</p>
<pre><code>yarn add vue2-touch-events
</code></pre><p>Next we add the new <code>v-touch</code> attribute and a handler method to the <strong>Card.</strong> This takes care of the events emitted by swipes.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Fantastic, we made it! We had the vision of building an encapsulated and reusable <strong>Carousel</strong> component, and we did!</p>
<p>What we still could add to improve the UX are some swipe animation when browsing the cards.</p>
<p>Thanks for reading! _If you like this blog post, Follow me on Twitter <a target="_blank" href="https://twitter.com/Fa_Hinse">@Fa_Hinse</a> and_ <em>please clap?</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to write simple modern JavaScript apps with Webpack and progressive web techniques ]]>
                </title>
                <description>
                    <![CDATA[ By Anurag Majumdar Have you thought about making modern JavaScript applications with the simplest setup possible for your next project? If so, you have come to the right place! JavaScript frameworks exist to help us build applications in a generalize... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-simple-modern-javascript-apps-with-webpack-and-progressive-web-techniques-a30354eab214/</link>
                <guid isPermaLink="false">66c356597ef110ecbf367ae9</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 26 Feb 2019 18:14:53 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*x8FsCF_x1ZiNhJzGoTyM8A.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Anurag Majumdar</p>
<p>Have you thought about making modern JavaScript applications with the simplest setup possible for your next project?</p>
<p>If so, you have come to the right place!</p>
<p>JavaScript frameworks exist to help us build applications in a generalized way with most of the common features. But most of the applications may not need all the powerful features of a framework. It may be overkill to just use a framework for specific requirements (especially small to medium scale projects).</p>
<p>Today I am going to show an approach to how you can use modern features and build your own customized Web Applications. You can also build your own framework on top of the sample applications if you want to. That is purely optional. The power of Vanilla JavaScript enables us to follow our own coding style irrespective of the tools used.</p>
<h3 id="heading-what-we-need">What We Need</h3>
<p>Before starting out, let us quickly skim through the features we need.</p>
<h4 id="heading-architectural-planning">Architectural Planning</h4>
<p>To ensure fast loading and consistent experiences, we’ll use the following patterns:</p>
<ul>
<li>Application Shell Architecture</li>
<li>PRPL (<strong>P</strong>ush, <strong>R</strong>ender, <strong>P</strong>re-cache, <strong>L</strong>azy loading) pattern</li>
</ul>
<h4 id="heading-build-setup">Build Setup</h4>
<p>We need a good custom build setup, so we will be using Webpack with the following requirements:</p>
<ul>
<li>ES6 &amp; Dynamic Imports support</li>
<li>SASS &amp; CSS support</li>
<li>Custom development &amp; production setup</li>
<li>Custom Service Worker Build</li>
</ul>
<h4 id="heading-bare-minimum-javascript-features">Bare Minimum JavaScript Features</h4>
<p>We will be touching on minimal JavaScript features to get us off the ground and produce the output we require. I will show you how we can use existing JavaScript ES6 features in our day to day vanilla applications. Here they are:</p>
<ul>
<li>ES6 Modules</li>
<li>Dynamic Imports</li>
<li>Object Literal Syntax Or ES6 Class Syntax</li>
<li>ES6 Arrow Functions</li>
<li>ES6 Template Literals</li>
</ul>
<p>At the end of this article there is a sample application demo along with its source code on GitHub. Let’s dig deeper, shall we? ?</p>
<h3 id="heading-architectural-planning-1">Architectural Planning</h3>
<p>The advent of <strong>Progressive Web Applications</strong> has helped bring new architectures in order to make our first paint more effective. Combining <strong>App Shell</strong> and <strong>PRPL</strong> patterns can result in consistent responsiveness and app-like experiences.</p>
<h4 id="heading-what-is-app-shell-amp-prpl">What is App Shell &amp; PRPL?</h4>
<p><strong>App Shell</strong> is an architectural pattern for building <strong>Progressive Web Applications</strong> where you ship the minimal <strong>critical resources</strong> in order to load your site. This basically consists of all the necessary resources for the first paint. You may cache the critical resources as well using a service worker.</p>
<p><strong>PRPL</strong> refers to the following:</p>
<ul>
<li><strong>P</strong>ush critical resources (especially using HTTP/2) for the initial route.</li>
<li><strong>R</strong>ender the initial route.</li>
<li><strong>P</strong>re-cache remaining routes or assets.</li>
<li><strong>L</strong>azy load portions of an application as and when required (especially when required by a user).</li>
</ul>
<h4 id="heading-what-do-these-architectures-look-like-in-code">What Do These Architectures Look Like In Code?</h4>
<p>The <strong>App Shell</strong> and <strong>PRPL</strong> pattern are both used together to achieve the best practices.</p>
<p>The App shell looks somewhat like the following piece of code:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"ie=edge"</span> /&gt;</span>
    <span class="hljs-comment">&lt;!-- Critical Styles --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">html</span> {
            <span class="hljs-attribute">box-sizing</span>: border-box;
        }

        *,
        *<span class="hljs-selector-pseudo">:after</span>,
        *<span class="hljs-selector-pseudo">:before</span> {
            <span class="hljs-attribute">box-sizing</span>: inherit;
        }

        <span class="hljs-selector-tag">body</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">font</span>: <span class="hljs-number">18px</span> <span class="hljs-string">'Oxygen'</span>, Helvetica;
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#ececec</span>;
        }

        <span class="hljs-selector-tag">header</span> {
            <span class="hljs-attribute">height</span>: <span class="hljs-number">60px</span>;
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#512DA8</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
            <span class="hljs-attribute">display</span>: flex;
            <span class="hljs-attribute">align-items</span>: center;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span> <span class="hljs-number">40px</span>;
            <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">1px</span> <span class="hljs-number">2px</span> <span class="hljs-number">6px</span> <span class="hljs-number">0px</span> <span class="hljs-number">#777</span>;
        }

        <span class="hljs-selector-tag">h1</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
        }

        <span class="hljs-selector-class">.banner</span> {
            <span class="hljs-attribute">text-decoration</span>: none;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
            <span class="hljs-attribute">cursor</span>: pointer;
        }

        <span class="hljs-selector-tag">main</span> {
            <span class="hljs-attribute">display</span>: flex;
            <span class="hljs-attribute">justify-content</span>: center;
            <span class="hljs-attribute">height</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100vh</span> - <span class="hljs-number">140px</span>);
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span> <span class="hljs-number">40px</span>;
            <span class="hljs-attribute">overflow-y</span>: auto;
        }

        <span class="hljs-selector-tag">button</span> {
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#512DA8</span>;
            <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#512DA8</span>;
            <span class="hljs-attribute">cursor</span>: pointer;
            <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">1px</span> <span class="hljs-number">1px</span> <span class="hljs-number">3px</span> <span class="hljs-number">0px</span> <span class="hljs-number">#777</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">15px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">20px</span>;
        }

        <span class="hljs-selector-class">.button</span> {
            <span class="hljs-attribute">display</span>: flex;
            <span class="hljs-attribute">justify-content</span>: center;
        }

        <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
            <span class="hljs-attribute">box-shadow</span>: none;
        }

        <span class="hljs-selector-tag">footer</span> {
            <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>;
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#2d3850</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
            <span class="hljs-attribute">display</span>: flex;
            <span class="hljs-attribute">align-items</span>: center;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">40px</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vanilla Todos PWA<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Main Application Section --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"banner"</span>&gt;</span> Vanilla Todos PWA <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span><span class="hljs-symbol">&amp;copy;</span> 2019 Anurag Majumdar - Vanilla Todos SPA<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Critical Scripts --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">async</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"&lt;%= htmlWebpackPlugin.files.chunks.main.entry %&gt;"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">noscript</span>&gt;</span>
            This site uses JavaScript. Please enable JavaScript in your browser.
        <span class="hljs-tag">&lt;/<span class="hljs-name">noscript</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You can see that the application shell consists of the bare minimum markup as a skeleton.</p>
<p><strong>Lines 9–82</strong>: Critical styles have been introduced into markup to ensure direct parsing of CSS instead of linking it to another file.</p>
<p><strong>Lines 89–96</strong>: Main application shell markup; these areas will be later manipulated by JavaScript (especially, the contents inside the main tag of line 93).</p>
<p><strong>Line 99</strong>: This is where the scripts come into play. The <strong>async</strong> attribute helps in not blocking the parser while the scripts get downloaded.</p>
<p>The app shell also enforces <strong>Push</strong> &amp; <strong>Render</strong> stages of the <strong>PR</strong>PL pattern. This happens when HTML is parsed by the browser to form pixels on the screen. It readily finds all the critical resources. Also, the <strong>critical scripts</strong> are responsible for showing the <strong>initial route</strong> by DOM manipulation (<strong>Render</strong>).</p>
<p>However, if we do not use a Service worker to cache the shell, it won’t be of any use for future reloads and performance benefits.</p>
<p>The following code snippet shows a service worker which caches the shell and all the static assets required for the application.</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> staticAssetsCacheName = <span class="hljs-string">'todo-assets-v3'</span>;
<span class="hljs-keyword">var</span> dynamicCacheName = <span class="hljs-string">'todo-dynamic-v3'</span>;

self.addEventListener(<span class="hljs-string">'install'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
    self.skipWaiting();
    event.waitUntil(
      caches.open(staticAssetsCacheName).then(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">cache</span>) </span>{
        cache.addAll([
            <span class="hljs-string">'/'</span>,
            <span class="hljs-string">"chunks/todo.d41d8cd98f00b204e980.js"</span>,<span class="hljs-string">"index.html"</span>,<span class="hljs-string">"main.d41d8cd98f00b204e980.js"</span>
        ]
        );
      }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error caching static assets:'</span>, error);
      })
    );
  });

  self.addEventListener(<span class="hljs-string">'activate'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
    <span class="hljs-keyword">if</span> (self.clients &amp;&amp; clients.claim) {
      clients.claim();
    }
    event.waitUntil(
      caches.keys().then(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">cacheNames</span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(
          cacheNames.filter(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">cacheName</span>) </span>{
            <span class="hljs-keyword">return</span> (cacheName.startsWith(<span class="hljs-string">'todo-'</span>)) &amp;&amp; cacheName !== staticAssetsCacheName;
          })
          .map(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">cacheName</span>) </span>{
            <span class="hljs-keyword">return</span> caches.delete(cacheName);
          })
        ).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Some error occurred while removing existing cache:'</span>, error);
        });
      }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Some error occurred while removing existing cache:'</span>, error);
    }));
  });

  self.addEventListener(<span class="hljs-string">'fetch'</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    event.respondWith(
      caches.match(event.request).then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> response || fetch(event.request)
          .then(<span class="hljs-function">(<span class="hljs-params">fetchResponse</span>) =&gt;</span> {
              <span class="hljs-keyword">return</span> cacheDynamicRequestData(dynamicCacheName, event.request.url, fetchResponse.clone());
          }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
            <span class="hljs-built_in">console</span>.log(error);
          });
      }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(error);
      })
    );
  });

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cacheDynamicRequestData</span>(<span class="hljs-params">dynamicCacheName, url, fetchResponse</span>) </span>{
    <span class="hljs-keyword">return</span> caches.open(dynamicCacheName)
      .then(<span class="hljs-function">(<span class="hljs-params">cache</span>) =&gt;</span> {
        cache.put(url, fetchResponse.clone());
        <span class="hljs-keyword">return</span> fetchResponse;
      }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(error);
      });
  }
</code></pre>
<p><strong>Lines 4–17</strong>: The install event of service workers helps to cache all the static assets. Here, you can cache the app shell resources (CSS, JavaScript, images, etc.) for the first route (as per App shell). Also, you can cache the remainder of the assets of the application ensuring the whole app can run offline too. This caching of static assets apart from the main app shell ensures the <strong>Pre-cache</strong> stage of the PR<strong>P</strong>L pattern.</p>
<p><strong>Lines 19–38:</strong> The activate event is the place for cleaning up of unused caches.</p>
<p><strong>Lines 40–63</strong>: These lines of code help in fetching resources from the cache if they are in cache or go to network. Also, if a network call is made, then the resource is not in cache and put into a new separate cache. This scenario helps to cache all dynamic data for an application.</p>
<p>All in all, most of the parts of the architecture have been covered. The only part left is the <strong>Lazy loading</strong> stage of the PRP<strong>L</strong> pattern. I will discuss this with regards to JavaScript.</p>
<h3 id="heading-our-build-setup">Our Build Setup</h3>
<p>What is a good architectural structure without a build setup? Webpack to the rescue. There are other tools like Parcel, Rollup etc. out there, but whatever concepts we apply to Webpack can be applied to any such tool.</p>
<p>I will map the concepts used to the plugins so that you can get hold of the basics used for setting up of the workflow. This is the most important step to getting started with a good reusable build config for your own application for the future.</p>
<p>I know how difficult it is for developers like us to configure Webpack or any tool for that matter from scratch. The following article was an inspiration which helped me to create my own build setup:</p>
<p><a target="_blank" href="https://hackernoon.com/a-tale-of-webpack-4-and-how-to-finally-configure-it-in-the-right-way-4e94c8e7e5c1">A tale of Webpack 4 and how to finally configure it in the right way. Updated.</a></p>
<p>Do refer to the above link if you get stuck anywhere with the build setup. For now let us check out the concepts required for the build.</p>
<h4 id="heading-es6-amp-dynamic-imports-support">ES6 &amp; Dynamic Imports support</h4>
<p><strong>Babel</strong> is a popular transpiler which is there to help us with transpiling ES6 features down to ES5. We will need the following packages to enable babel working with webpack:</p>
<ul>
<li>@babel/core</li>
<li>@babel/plugin-syntax-dynamic-import</li>
<li>@babel/preset-env</li>
<li>babel-core</li>
<li>babel-loader</li>
<li>babel-preset-env</li>
</ul>
<p>Here is a sample babelrc for reference:</p>
<pre><code class="lang-js">{
    <span class="hljs-string">"presets"</span>: [<span class="hljs-string">"@babel/preset-env"</span>],
    <span class="hljs-string">"plugins"</span>: [<span class="hljs-string">"@babel/plugin-syntax-dynamic-import"</span>]
}
</code></pre>
<p>During babel setup, we need to feed the following <strong>2nd line</strong> in presets to enable babel to transpile ES6 down to ES5 and the <strong>3rd line</strong> in plugins to enable the dynamic import support with Webpack.</p>
<p>Here is how babel is used with Webpack:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-attr">entry</span>: {
        <span class="hljs-comment">// Mention Entry File</span>
    },
    <span class="hljs-attr">output</span>: {
        <span class="hljs-comment">// Mention Output Filenames</span>
    },
    <span class="hljs-attr">module</span>: {
        <span class="hljs-attr">rules</span>: [
            {
                <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.js$/</span>,
                exclude: <span class="hljs-regexp">/node_modules/</span>,
                use: {
                    <span class="hljs-attr">loader</span>: <span class="hljs-string">'babel-loader'</span>
                }
            }
        ]
    },
    <span class="hljs-attr">plugins</span>: [
        <span class="hljs-comment">// Plugins</span>
    ]
};
</code></pre>
<p><strong>Lines 10–17</strong>: The babel loader is used to set up the babel transpilation process in webpack.config.js. For simplicity, the other parts of the config have been eliminated or commented out.</p>
<h4 id="heading-sass-amp-css-support">SASS &amp; CSS Support</h4>
<p>For setting up SASS and CSS you need the following packages:</p>
<ul>
<li>sass-loader</li>
<li>css-loader</li>
<li>style-loader</li>
<li>MiniCssExtractPlugin</li>
</ul>
<p>Here is how the config looks like:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-attr">entry</span>: {
        <span class="hljs-comment">// Mention Entry File</span>
    },
    <span class="hljs-attr">output</span>: {
        <span class="hljs-comment">// Mention Output Filenames</span>
    },
    <span class="hljs-attr">module</span>: {
        <span class="hljs-attr">rules</span>: [
            {
                <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.js$/</span>,
                exclude: <span class="hljs-regexp">/node_modules/</span>,
                use: {
                    <span class="hljs-attr">loader</span>: <span class="hljs-string">'babel-loader'</span>
                }
            },
            {
                <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.scss$/</span>,
                use: [
                    <span class="hljs-string">'style-loader'</span>,
                    MiniCssExtractPlugin.loader,
                    <span class="hljs-string">'css-loader'</span>,
                    <span class="hljs-string">'sass-loader'</span>
                ]
            }
        ]
    },
    <span class="hljs-attr">plugins</span>: [
        <span class="hljs-keyword">new</span> MiniCssExtractPlugin({
            <span class="hljs-attr">filename</span>: <span class="hljs-string">'[name].css'</span>
        }),
    ]
};
</code></pre>
<p><strong>Lines 17–25</strong>: This is the area where the loaders are registered.</p>
<p><strong>Lines 29–31</strong>: Since we are using a plugin to extract a CSS file, we are using the <strong>MiniCssExtractPlugin</strong> here.</p>
<h4 id="heading-custom-development-amp-production-setup">Custom Development &amp; Production Setup</h4>
<p>This is the most important section of the build process. We all know that we need a development and production build setup for developing applications and also deploying the final distributable to the web.</p>
<p>Here are the packages that will get used:</p>
<ul>
<li><strong>clean-webpack-plugin</strong>: For cleanup of the dist folder contents.</li>
<li><strong>compression-webpack-plugin</strong>: For gzipping the dist folder file contents.</li>
<li><strong>copy-webpack-plugin</strong>: For copying static assets, files or resources from application source to dist folder.</li>
<li><strong>html-webpack-plugin</strong>: For creating an index.html file in the dist folder.</li>
<li><strong>webpack-md5-hash</strong>: For hashing application source files in the dist folder.</li>
<li><strong>webpack-dev-server</strong>: For running a local development server.</li>
</ul>
<p>Here is the final Webpack config file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);
<span class="hljs-keyword">const</span> MiniCssExtractPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'mini-css-extract-plugin'</span>);
<span class="hljs-keyword">const</span> HtmlWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'html-webpack-plugin'</span>);
<span class="hljs-keyword">const</span> WebpackMd5Hash = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack-md5-hash'</span>);
<span class="hljs-keyword">const</span> CleanWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'clean-webpack-plugin'</span>);
<span class="hljs-keyword">const</span> CopyWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'copy-webpack-plugin'</span>);
<span class="hljs-keyword">const</span> CompressionPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'compression-webpack-plugin'</span>);

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">env, argv</span>) =&gt;</span> ({
    <span class="hljs-attr">entry</span>: {
        <span class="hljs-attr">main</span>: <span class="hljs-string">'./src/main.js'</span>
    },
    <span class="hljs-attr">devtool</span>: argv.mode === <span class="hljs-string">'production'</span> ? <span class="hljs-literal">false</span> : <span class="hljs-string">'source-map'</span>,
    <span class="hljs-attr">output</span>: {
        <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">'dist'</span>),
        <span class="hljs-attr">chunkFilename</span>:
            argv.mode === <span class="hljs-string">'production'</span>
                ? <span class="hljs-string">'chunks/[name].[chunkhash].js'</span>
                : <span class="hljs-string">'chunks/[name].js'</span>,
        <span class="hljs-attr">filename</span>:
            argv.mode === <span class="hljs-string">'production'</span> ? <span class="hljs-string">'[name].[chunkhash].js'</span> : <span class="hljs-string">'[name].js'</span>
    },
    <span class="hljs-attr">module</span>: {
        <span class="hljs-attr">rules</span>: [
            {
                <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.js$/</span>,
                exclude: <span class="hljs-regexp">/node_modules/</span>,
                use: {
                    <span class="hljs-attr">loader</span>: <span class="hljs-string">'babel-loader'</span>
                }
            },
            {
                <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.scss$/</span>,
                use: [
                    <span class="hljs-string">'style-loader'</span>,
                    MiniCssExtractPlugin.loader,
                    <span class="hljs-string">'css-loader'</span>,
                    <span class="hljs-string">'sass-loader'</span>
                ]
            }
        ]
    },
    <span class="hljs-attr">plugins</span>: [
        <span class="hljs-keyword">new</span> CleanWebpackPlugin(<span class="hljs-string">'dist'</span>, {}),
        <span class="hljs-keyword">new</span> MiniCssExtractPlugin({
            <span class="hljs-attr">filename</span>:
                argv.mode === <span class="hljs-string">'production'</span>
                    ? <span class="hljs-string">'[name].[contenthash].css'</span>
                    : <span class="hljs-string">'[name].css'</span>
        }),
        <span class="hljs-keyword">new</span> HtmlWebpackPlugin({
            <span class="hljs-attr">inject</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">hash</span>: <span class="hljs-literal">true</span>,
            <span class="hljs-attr">template</span>: <span class="hljs-string">'./index.html'</span>,
            <span class="hljs-attr">filename</span>: <span class="hljs-string">'index.html'</span>
        }),
        <span class="hljs-keyword">new</span> WebpackMd5Hash(),
        <span class="hljs-keyword">new</span> CopyWebpackPlugin([
            <span class="hljs-comment">// {</span>
            <span class="hljs-comment">//     from: './src/assets',</span>
            <span class="hljs-comment">//     to: './assets'</span>
            <span class="hljs-comment">// },</span>
            <span class="hljs-comment">// {</span>
            <span class="hljs-comment">//     from: 'manifest.json',</span>
            <span class="hljs-comment">//     to: 'manifest.json'</span>
            <span class="hljs-comment">// }</span>
        ]),
        <span class="hljs-keyword">new</span> CompressionPlugin({
            <span class="hljs-attr">algorithm</span>: <span class="hljs-string">'gzip'</span>
        })
    ],
    <span class="hljs-attr">devServer</span>: {
        <span class="hljs-attr">contentBase</span>: <span class="hljs-string">'dist'</span>,
        <span class="hljs-attr">watchContentBase</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">port</span>: <span class="hljs-number">1000</span>
    }
});
</code></pre>
<p><strong>Lines 9–77:</strong> The entire webpack config is a function which takes two arguments. Here I have used the <strong>argv</strong> i.e., the arguments sent while running the webpack or webpack-dev-server commands.</p>
<p>The below image shows the scripts section in package.json.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*99lCHt0UnbFBlTcfFjcgNg.png" alt="Image" width="800" height="327" loading="lazy">
<em>npm scripts in package.json</em></p>
<p>Accordingly, if we run <strong>npm run build</strong> it will trigger a production build, and if we run <strong>npm run serve</strong> it will trigger a development flow with a local development server.</p>
<p><strong>Lines 44–77</strong>: These lines show how the plugins and the development server config needs to be setup.</p>
<p><strong>Lines 59–66</strong>: These lines are any resources or static assets which need to be copied over from the application source.</p>
<h4 id="heading-custom-service-worker-build">Custom Service Worker Build</h4>
<p>Since we all know how tedious it is to write the names of all the files again for caching, I made a custom service worker build script for catching hold of the files in the <strong>dist</strong> folder and then adding them as contents of the cache in the service worker template. Finally, the service worker file will get written to the <strong>dist</strong> folder.</p>
<p>The concepts regarding the service worker file we talked about will be the same. Here is the script in action:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> glob = <span class="hljs-built_in">require</span>(<span class="hljs-string">'glob'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

<span class="hljs-keyword">const</span> dest = <span class="hljs-string">'dist/sw.js'</span>;
<span class="hljs-keyword">const</span> staticAssetsCacheName = <span class="hljs-string">'todo-assets-v1'</span>;
<span class="hljs-keyword">const</span> dynamicCacheName = <span class="hljs-string">'todo-dynamic-v1'</span>;

<span class="hljs-keyword">let</span> staticAssetsCacheFiles = glob
    .sync(<span class="hljs-string">'dist/**/*'</span>)
    .map(<span class="hljs-function">(<span class="hljs-params">path</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> path.slice(<span class="hljs-number">5</span>);
    })
    .filter(<span class="hljs-function">(<span class="hljs-params">file</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (<span class="hljs-regexp">/\.gz$/</span>.test(file)) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        <span class="hljs-keyword">if</span> (<span class="hljs-regexp">/sw\.js$/</span>.test(file)) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        <span class="hljs-keyword">if</span> (!<span class="hljs-regexp">/\.+/</span>.test(file)) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    });

<span class="hljs-keyword">const</span> stringFileCachesArray = <span class="hljs-built_in">JSON</span>.stringify(staticAssetsCacheFiles);

<span class="hljs-keyword">const</span> serviceWorkerScript = <span class="hljs-string">`var staticAssetsCacheName = '<span class="hljs-subst">${staticAssetsCacheName}</span>';
var dynamicCacheName = '<span class="hljs-subst">${dynamicCacheName}</span>';
self.addEventListener('install', function (event) {
    self.skipWaiting();
    event.waitUntil(
      caches.open(staticAssetsCacheName).then(function (cache) {
        cache.addAll([
            '/',
            <span class="hljs-subst">${stringFileCachesArray.slice(<span class="hljs-number">1</span>, stringFileCachesArray.length - <span class="hljs-number">1</span>)}</span>
        ]
        );
      }).catch((error) =&gt; {
        console.log('Error caching static assets:', error);
      })
    );
  });
  self.addEventListener('activate', function (event) {
    if (self.clients &amp;&amp; clients.claim) {
      clients.claim();
    }
    event.waitUntil(
      caches.keys().then(function (cacheNames) {
        return Promise.all(
          cacheNames.filter(function (cacheName) {
            return (cacheName.startsWith('todo-')) &amp;&amp; cacheName !== staticAssetsCacheName;
          })
          .map(function (cacheName) {
            return caches.delete(cacheName);
          })
        ).catch((error) =&gt; {
            console.log('Some error occurred while removing existing cache:', error);
        });
      }).catch((error) =&gt; {
        console.log('Some error occurred while removing existing cache:', error);
    }));
  });
  self.addEventListener('fetch', (event) =&gt; {
    event.respondWith(
      caches.match(event.request).then((response) =&gt; {
        return response || fetch(event.request)
          .then((fetchResponse) =&gt; {
              return cacheDynamicRequestData(dynamicCacheName, event.request.url, fetchResponse.clone());
          }).catch((error) =&gt; {
            console.log(error);
          });
      }).catch((error) =&gt; {
        console.log(error);
      })
    );
  });
  function cacheDynamicRequestData(dynamicCacheName, url, fetchResponse) {
    return caches.open(dynamicCacheName)
      .then((cache) =&gt; {
        cache.put(url, fetchResponse.clone());
        return fetchResponse;
      }).catch((error) =&gt; {
        console.log(error);
      });
  }
`</span>;

fs.writeFile(dest, serviceWorkerScript, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>) </span>{
    <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span>;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Service Worker Write success'</span>);
});
</code></pre>
<p><strong>Lines 8–18</strong>: This is the place where all the contents of the dist folder are captured as an array <strong>staticAssetsCacheFiles.</strong></p>
<p><strong>Lines 22–85</strong>: This is the service worker template we talked about before. The concepts are exactly the same, just that we are introducing variables into the template so that we can reuse the service worker template and make it handy for future use. This template was also required since we needed to add <strong>dist</strong> folder contents to the cache as per <strong>line 33</strong>.</p>
<p><strong>Lines 87–90</strong>: Finally, a new service worker file will get written to the <strong>dist</strong> folder along with its contents from the service worker template <strong>serviceWorkerScript</strong>.</p>
<p>The command to run the above script is <strong>node build-sw</strong> and it should be run after <strong>webpack --mode production</strong> is done.</p>
<p>This service worker build script really helped me a lot in caching files easily. I am currently using this for my own side projects due to its simplicity and great ease of tackling the caching problem.</p>
<p>If you guys want to use a library for Progressive Web Application related features, you can go for <a target="_blank" href="https://developers.google.com/web/tools/workbox/">Workbox</a>. This library does some real neat stuff and has amazing features which you can take control of.</p>
<h4 id="heading-final-look-at-the-packages">Final Look At The Packages</h4>
<p>Here is a sample package.json file with all dependencies:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"vanilla-todos-pwa"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"A simple todo application using ES6 and Webpack"</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"src/main.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"webpack --mode production &amp;&amp; node build-sw"</span>,
    <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"webpack-dev-server --mode=development --hot"</span>
  },
  <span class="hljs-attr">"keywords"</span>: [],
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Anurag Majumdar"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@babel/core"</span>: <span class="hljs-string">"^7.2.2"</span>,
    <span class="hljs-attr">"@babel/plugin-syntax-dynamic-import"</span>: <span class="hljs-string">"^7.2.0"</span>,
    <span class="hljs-attr">"@babel/preset-env"</span>: <span class="hljs-string">"^7.2.3"</span>,
    <span class="hljs-attr">"autoprefixer"</span>: <span class="hljs-string">"^9.4.5"</span>,
    <span class="hljs-attr">"babel-core"</span>: <span class="hljs-string">"^6.26.3"</span>,
    <span class="hljs-attr">"babel-loader"</span>: <span class="hljs-string">"^8.0.4"</span>,
    <span class="hljs-attr">"babel-preset-env"</span>: <span class="hljs-string">"^1.7.0"</span>,
    <span class="hljs-attr">"clean-webpack-plugin"</span>: <span class="hljs-string">"^1.0.0"</span>,
    <span class="hljs-attr">"compression-webpack-plugin"</span>: <span class="hljs-string">"^2.0.0"</span>,
    <span class="hljs-attr">"copy-webpack-plugin"</span>: <span class="hljs-string">"^4.6.0"</span>,
    <span class="hljs-attr">"css-loader"</span>: <span class="hljs-string">"^2.1.0"</span>,
    <span class="hljs-attr">"html-webpack-plugin"</span>: <span class="hljs-string">"^3.2.0"</span>,
    <span class="hljs-attr">"mini-css-extract-plugin"</span>: <span class="hljs-string">"^0.5.0"</span>,
    <span class="hljs-attr">"node-sass"</span>: <span class="hljs-string">"^4.11.0"</span>,
    <span class="hljs-attr">"sass-loader"</span>: <span class="hljs-string">"^7.1.0"</span>,
    <span class="hljs-attr">"style-loader"</span>: <span class="hljs-string">"^0.23.1"</span>,
    <span class="hljs-attr">"terser"</span>: <span class="hljs-string">"^3.14.1"</span>,
    <span class="hljs-attr">"webpack"</span>: <span class="hljs-string">"^4.28.4"</span>,
    <span class="hljs-attr">"webpack-cli"</span>: <span class="hljs-string">"^3.2.1"</span>,
    <span class="hljs-attr">"webpack-dev-server"</span>: <span class="hljs-string">"^3.1.14"</span>,
    <span class="hljs-attr">"webpack-md5-hash"</span>: <span class="hljs-string">"0.0.6"</span>
  }
}
</code></pre>
<p>Remember that Webpack gets updated frequently, and changes keep happening in the community with new plugins replacing existing ones. So it is important to keep a note of the concepts required for a build setup rather than the actual packages used.</p>
<h3 id="heading-javascript-features">JavaScript Features</h3>
<p>We all have a choice: either to write our own framework for certain features to be used by our application such as change detection, routing, storage patterns, redux etc, or pull already existing packages for such features.</p>
<p>Now I’ll speak about the bare minimum features required in order to structure the layout of our application and get it going. Later on you can add your own frameworks or packages to the application.</p>
<h4 id="heading-es6-modules">ES6 Modules</h4>
<p>We will use ES6 import and export statements and treat each file as an ES6 module. This feature is commonly used by popular frameworks like Angular and React and is pretty handy. With the power of our Webpack config, we can fully utilize the power of import and export statements.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { appTemplate } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.template'</span>;
<span class="hljs-keyword">import</span> { AppModel } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.model'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AppComponent = {
  <span class="hljs-comment">// App Component code here...</span>
};
</code></pre>
<h4 id="heading-object-literal-syntax-or-es6-class-syntax">Object Literal Syntax Or ES6 Class Syntax</h4>
<p>Building components is a very important part of our application. We can choose to go with the latest web standards like Web Components too, but to keep things simple we can go ahead and use object literal syntax or ES6 class syntax.</p>
<p>The only thing with class syntax is that we need to instantiate it and then export it. So to keep things even simpler, I went ahead with object literal syntax for component architecture.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { appTemplate } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.template'</span>;
<span class="hljs-keyword">import</span> { AppModel } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.model'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AppComponent = {

    init() {
        <span class="hljs-built_in">this</span>.appElement = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#app'</span>);
        <span class="hljs-built_in">this</span>.initEvents();
        <span class="hljs-built_in">this</span>.render();
    },

    initEvents() {
        <span class="hljs-built_in">this</span>.appElement.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
            <span class="hljs-keyword">if</span> (event.target.className === <span class="hljs-string">'btn-todo'</span>) {
                <span class="hljs-keyword">import</span>( <span class="hljs-comment">/* webpackChunkName: "todo" */</span> <span class="hljs-string">'./todo/todo.module'</span>)
                    .then(<span class="hljs-function"><span class="hljs-params">lazyModule</span> =&gt;</span> {
                        lazyModule.TodoModule.init();
                    })
                    .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-string">'An error occurred while loading Module'</span>);
            }
        });

        <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.banner'</span>).addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
            event.preventDefault();
            <span class="hljs-built_in">this</span>.render();
        });
    },

    render() {
        <span class="hljs-built_in">this</span>.appElement.innerHTML = appTemplate(AppModel);
    }
};
</code></pre>
<p><strong>Lines 4–32:</strong> We export an object called <strong>AppComponent</strong> which is immediately available for use in other parts of our application.</p>
<p>You can go ahead and use ES6 class syntax or standard Web Components too and achieve a more declarative way of writing code here. For simplicity’s sake, I chose to write the demo application in a more imperative approach.</p>
<h4 id="heading-dynamic-imports">Dynamic Imports</h4>
<p>Remember I talked about missing out on the “L” of the <strong>PRPL</strong> pattern? Dynamic import is the way to go ahead and lazy load our components or modules. Since we used the <strong>App Shell</strong> and <strong>PRPL</strong> together to cache the shell and other route assets, dynamic imports import the lazy component or module from the cache instead of the network.</p>
<p>Note that if we only used <strong>App Shell</strong> architecture, the remaining assets of the application i.e., the contents of <strong>chunks</strong> folder, would not have been cached.</p>
<p><strong>Lines 15–19:</strong> Refer to App Component code; this is the place where dynamic imports shine. If we click on a button having the class <strong>btn-todo,</strong> then only this <strong>TodoModule</strong> gets loaded. By the way, <strong>TodoModule</strong> is just another JavaScript file which consists of a set of object components.</p>
<h4 id="heading-es6-arrow-functions-amp-es6-template-literals">ES6 Arrow Functions &amp; ES6 Template Literals</h4>
<p>Arrow functions should be used especially where we want to make sure of the <strong>this</strong> keyword inside the function, which should refer to the surrounding context where the arrow function is declared. Apart from that, these functions really help in creating neat shorthand syntax.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> appTemplate = <span class="hljs-function"><span class="hljs-params">model</span> =&gt;</span> <span class="hljs-string">`
    &lt;section class="app"&gt;
        &lt;h3&gt; <span class="hljs-subst">${model.title}</span> &lt;/h3&gt;
        &lt;section class="button"&gt;
            &lt;button class="btn-todo"&gt; Todo Module &lt;/button&gt;
        &lt;/section&gt;
    &lt;/section&gt;
`</span>;
</code></pre>
<p>The above example is a template function defined as an arrow function which accepts a model and returns an HTML string consisting of the model data in it. String interpolation is carried out with the help of <strong>ES6 template literals</strong>. The real benefit of template literals is <strong>multi-line strings</strong> and <strong>interpolation</strong> of model data into the string.</p>
<p>Here’s a micro tip for handling component templating and generation of reusable components: use the <strong>reduce</strong> function to accumulate all HTML strings as per the following example:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> WorkModel = [
    {
        <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">src</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">alt</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">designation</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">period</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">description</span>: <span class="hljs-string">''</span>
    },
    {
        <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
        <span class="hljs-attr">src</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">alt</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">designation</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">period</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">description</span>: <span class="hljs-string">''</span>
    },
    <span class="hljs-comment">//...</span>
];


<span class="hljs-keyword">const</span> workCardTemplate = <span class="hljs-function">(<span class="hljs-params">cardModel</span>) =&gt;</span> <span class="hljs-string">`
&lt;section id="<span class="hljs-subst">${cardModel.id}</span>" class="work-card"&gt;
    &lt;section class="work__image"&gt;
        &lt;img class="work__image-content" type="image/svg+xml" src="<span class="hljs-subst">${
            cardModel.src
        }</span>" alt="<span class="hljs-subst">${cardModel.alt}</span>" /&gt;
    &lt;/section&gt;
    &lt;section class="work__designation"&gt;<span class="hljs-subst">${cardModel.designation}</span>&lt;/section&gt;
    &lt;section class="work__period"&gt;<span class="hljs-subst">${cardModel.period}</span>&lt;/section&gt;
    &lt;section class="work__content"&gt;
        &lt;section class="work__content-text"&gt;
            <span class="hljs-subst">${cardModel.description}</span>
        &lt;/section&gt;
    &lt;/section&gt;
&lt;/section&gt;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> workTemplate = <span class="hljs-function">(<span class="hljs-params">model</span>) =&gt;</span> <span class="hljs-string">`
&lt;section class="work__section"&gt;
    &lt;section class="work-text"&gt;
        &lt;header class="header-text"&gt;
            &lt;span class="work-text__header"&gt; Work &lt;/span&gt;
        &lt;/header&gt;
        &lt;section class="work-text__content content-text"&gt;
            &lt;p class="work-text__content-para"&gt;
                This area signifies work experience
            &lt;/p&gt;
        &lt;/section&gt;
    &lt;/section&gt;
    &lt;section class="work-cards"&gt;
        <span class="hljs-subst">${model.reduce((html, card) =&gt; html + workCardTemplate(card), <span class="hljs-string">''</span>)}</span>
    &lt;/section&gt;
&lt;/section&gt;
`</span>;
</code></pre>
<p>The above piece of code does a great deal of work indeed. Simple yet intuitive. It does follow a little inspiration from the frameworks out there.</p>
<p><strong>Lines 1–19</strong>: This is a sample model array on which the reduce function can run in order to give the reusable template feature.</p>
<p><strong>Line 53:</strong> This line does all the magic in generating multiple reusable components into one HTML string. The reduce function takes in the accumulator as the first argument, and each value of the array as the second argument.</p>
<p>Thanks to these simple features, we already have an application structure in place. The best way to learn a feature is to put it in action they say, so here we are. ?</p>
<h3 id="heading-application-demo">Application Demo</h3>
<p>Congratulations on reaching here!</p>
<p>This post covered a lot of features indeed and getting hold of all the concepts and techniques will take some time.</p>
<p>Here is a demo of the to-do application built with all the features as discussed in this article. <a target="_blank" href="https://vanilla-todos-pwa.firebaseapp.com/">Click here</a> to visit the site.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*6MBoDHLD7IsyNxa1MeXB4Q.gif" alt="Image" width="1920" height="1080" loading="lazy">
<em>Vanilla Todos Demo</em></p>
<p><a target="_blank" href="https://github.com/anurag-majumdar/vanilla-todos-pwa">Click here</a> for the link to GitHub repository. Feel free to clone the repository and go through the code for a better understanding of the conceptual examples mentioned in the article.</p>
<h3 id="heading-sample-production-app">Sample Production App</h3>
<p>The production site is a portfolio which was designed, developed and engineered from scratch using the exact features as specified in this article. The <strong>Single Page Application</strong> is broken down into custom <strong>modules</strong> and <strong>components</strong>.</p>
<p>The flexibility and power that comes with <strong>Vanilla JavaScript</strong> is something unique and does help in producing some astonishing results.</p>
<p><a target="_blank" href="http://www.anurag-majumdar.com">Click here</a> to go to the site. Here is the site in action:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*rcY16O1cdX0ED3J3eUGPdQ.gif" alt="Image" width="1920" height="1080" loading="lazy">
<em>Custom Portfolio</em></p>
<p>Do visit the site to get a feel for it. The colors are not accurately produced in the demo here. The engineering put into this site produced the following results:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*kxp8u-ojMzi6sfLzSo5Bww.png" alt="Image" width="800" height="391" loading="lazy">
<em>Portfolio Lighthouse Results</em></p>
<p>Never scored a perfect 100 before in any subject. ?</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>There are several projects we might like to build using Vanilla JavaScript instead of frameworks in order to achieve certain results quickly. I wrote this article to help developers use a simple custom setup to build their future projects.</p>
<p>The best part about the Vanilla framework is that developers have the freedom to shape their engineering thought patterns according to various use cases. Be it Imperative or Declarative style of programming, creating or using of latest existing features. As long as we produce consistent and performant applications with good code maintainability, our job is done for the day.</p>
<p>Happy hacking! ?</p>
<h3 id="heading-other-posts-by-me">Other Posts By Me</h3>
<p>Find me at <a target="_blank" href="https://medium.com/@anurag.majumdar">https://medium.com/@anurag.majumdar</a></p>
<h4 id="heading-web-development">➥ Web Development</h4>
<ul>
<li><a target="_blank" href="https://medium.com/udacity-google-india-scholars/build-your-own-reusable-app-shell-from-scratch-7823f65e1fbd">Progressive Web App Shell: The Key To Loading Your Site Under 1 Second!</a></li>
<li><a target="_blank" href="https://medium.com/beginners-guide-to-mobile-web-development/super-and-extends-in-javascript-es6-understanding-the-tough-parts-6120372d3420">“Super” and “Extends” In JavaScript ES6 — Understanding The Tough Parts</a></li>
<li><a target="_blank" href="https://medium.com/beginners-guide-to-mobile-web-development/introduction-to-polyfills-their-usage-9cd6db4b1923">Introduction To Polyfills &amp; Their Usage</a></li>
</ul>
<h4 id="heading-life-event">➥ Life Event</h4>
<ul>
<li><a target="_blank" href="https://medium.com/@anurag.majumdar/udacitys-google-mobile-web-scholarship-challenge-and-its-glorious-effects-9cd4979f5053">Udacity’s Google Mobile Web Scholarship Challenge and its glorious effects!</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a custom PWA with Workbox in create-react-app ]]>
                </title>
                <description>
                    <![CDATA[ By Zaid Humayun Note: This is the third in a series of posts about PWAs inside of React. For a quick primer, see the previous two posts here and here. In this follow up post, I’m going to take you through how to build a custom Progressive Web App (PW... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-custom-pwa-with-workbox-in-create-react-app-be580686cf73/</link>
                <guid isPermaLink="false">66d461cf2472e5ed2fa07bd1</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 19 Sep 2018 19:23:09 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*Sp2Kk29yH_3VsOeB4i1dpg.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Zaid Humayun</p>
<p><strong>Note:</strong> This is the third in a series of posts about PWAs inside of React. For a quick primer, see the previous two posts <a target="_blank" href="https://medium.freecodecamp.org/how-to-build-a-pwa-with-create-react-app-and-custom-service-workers-376bd1fdc6d3">here</a> and <a target="_blank" href="https://medium.freecodecamp.org/how-to-customize-service-workers-with-create-react-app-4424dda6210c">here</a>.</p>
<p>In this follow up post, I’m going to take you through how to build a custom Progressive Web App (PWA) using <a target="_blank" href="https://developers.google.com/web/tools/workbox/">Google’s Workbox library</a> without ejecting from the create-react-app (CRA) shell.</p>
<p>Workbox is a collection of libraries that make it easier to build offline functionality. Workbox is also considered the successor to the <code>sw-precache</code> library, which CRA uses to generate a default SW.</p>
<p>There has been some talk about CRA migrating from <code>sw-precache</code> to Workbox (reference <a target="_blank" href="https://github.com/facebook/create-react-app/issues/2340">this issue</a> for details). Unfortunately, nothing seems to have come of it quite yet.</p>
<h3 id="heading-goals"><strong>Goals</strong></h3>
<ol>
<li>Configure the CRA build to use <a target="_blank" href="https://github.com/timarney/react-app-rewired">react-app-rewired</a>. (react-app-rewired is a library to configure the default CRA build without ejecting)</li>
<li>Use react-app-rewired to customize the build to use Workbox to generate a service worker</li>
<li>Build a very simple todo app</li>
<li>Implement offline functionality for the todo app using Workbox.<br>The offline functionality we will be targeting:<br>a) Cache retrieved assets so they can be served offline<br>b) Allow POSTing of data offline</li>
</ol>
<h3 id="heading-introducing-workbox-into-cra"><strong>Introducing Workbox into CRA</strong></h3>
<p>First, create a fresh CRA repository with the following command:</p>
<pre><code>npx create-react-app react-app-rewire-workbox
</code></pre><p>This should set up a new folder with the relevant name. Once you have this folder set up, cd into the folder and create a service worker file in the public folder. I’ll call mine <code>custom-service-worker.js</code>.</p>
<p>Once, you’ve done this, go ahead and remove the check for <code>NODE_ENV</code> being set to PRODUCTION inside of <code>registerServiceWorker.js</code></p>
<p>Finally, inside of the <code>custom-service-worker.js</code> file, paste the following code:</p>
<p>This code snippet is something I’ve picked up straight from the <a target="_blank" href="https://developers.google.com/web/tools/workbox/guides/get-started">Workbox website</a>. You use the <code>importScripts</code> line to inject a global variable named <code>workbox</code> into your file. The script your are importing is served via a CDN. You then have a simple check to see if the variable was loaded correctly by the script or not.</p>
<p>So, we now have Workbox working for us in a dev environment. Next, let’s figure out how to implement <code>react-app-rewired</code> into CRA.</p>
<h3 id="heading-implementing-react-app-rewired-in-cra"><strong>Implementing react-app-rewired In CRA</strong></h3>
<p>Add the <code>react-app-rewired</code> package to your project folder by using the following command:</p>
<pre><code>npm install --save-dev react-app-rewired
</code></pre><p>Now, if you read <a target="_blank" href="https://github.com/timarney/react-app-rewired">the docs</a>, they mention that you need to set up a <code>config-overrides.js</code> file in the root directory of your project. Let’s figure out what this does first.</p>
<p>I’ll set up a barebones file and explain to you what it means. There is a very detailed explanation of this in <a target="_blank" href="https://github.com/timarney/react-app-rewired#extended-configuration-options">the docs</a>, if you wish to read that instead.</p>
<p>You can export an object from this file with three keys: webpack, jest, devServer. The respective functions allow you to configure the webpack production server configuration, the jest configuration, and finally the webpack development server configuration.</p>
<p>If you look at the <code>devServer</code> key in the <code>config-overrides.js</code> file, you will notice that we are logging <code>configFunction.toString()</code> instead of just <code>configFunction</code> . This is because if you try the latter, Node will just print <code>[Function]</code> to the console.</p>
<p>Open up your <code>package.json</code> file and replace the scripts command for start with <code>react-app-rewired start</code> .</p>
<h3 id="heading-building-the-todo-app"><strong>Building The Todo App</strong></h3>
<p>So far, we have managed to introduce Workbox into our dev environment, and have also introduced <code>react-app-rewired</code> into our CRA shell. Let’s leave things as they are and build a sample todo app, and get it running in the dev environment.</p>
<p>The todo app is going to need a couple of moving pieces, just so we can actually make use of service workers.</p>
<p>It’s going to involve:</p>
<ol>
<li>A basic UI layer (I’m going to completely ignore styling for this.)</li>
<li>A <code>json-server</code> we can request data from</li>
</ol>
<p>I’m not going into too much detail about setting this up, because its all fairly straightforward. I’ll include a link to a git repo with a working version of this app at the end of this article, so you can have a look at that.</p>
<p>Here is the Todo component I have written.</p>
<p>The component makes a fetch request to a <code>json-server</code> I have set up, and gets a response consisting of an array of todos. The component then renders these todos. Like I said, extremely simple.</p>
<p>To set up the <code>json-server</code> run the following command:</p>
<pre><code>npm install --save json-server
</code></pre><p>Create a file called <code>db.json</code> with the following structure</p>
<p>Finally, run the following command in the terminal:</p>
<pre><code>json-server --watch db.json --port <span class="hljs-number">8000</span>
</code></pre><p>This runs a local server on port 8000, and watches the <code>db.json</code> file for any changes. In case anything changes, the server restarts itself. Its a very simple way to mock a server for testing your app.</p>
<p>Finally, update your <code>App.js</code> file to reflect your new Todo component, and remove the default JSX from that file.</p>
<p>Fire up the app (inside of an incognito window) and take a look at what it looks like now. You should see a list of todos and an input box below them with a button to submit. Like I said, very simple UI.</p>
<p>Once you’ve got all that set up, let’s figure out a way to make this stuff work offline using Workbox.</p>
<p><strong>Note:</strong> While testing service worker functionality in a dev environment, always make sure you do so within a new incognito window each time. It makes testing and debugging much less of a headache because your data is not stored across sessions.</p>
<h3 id="heading-implementing-caching-with-workbox"><strong>Implementing Caching With Workbox</strong></h3>
<p>Now, if you go ahead and open up the Chrome toolbar, you should see something that looks like the following under the Application tab.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*6M7SSHyjM_1Yf2GV8Dvk9A.png" alt="Image" width="800" height="425" loading="lazy">
<em>Google Chrome Developer Toolbar</em></p>
<p>Check the offline checkbox and then try to reload your webpage. It will probably fail with an error saying there was no network connection detected. If you look at the network tab, you will see a bunch of failed network requests.</p>
<p>The most obvious one that will fail is the request to our <code>json-server</code> to fetch the list of todos. Let’s fix that one first. Open up the <code>custom-service-worker.js</code> file and add in the following code</p>
<pre><code>workbox.routing.registerRoute(  <span class="hljs-string">'http://localhost:8000/todos'</span>,  workbox.strategies.networkFirst())
</code></pre><p>This is setting up a caching strategy of <code>networkFirst</code> for any requests made to the<code>http://localhost:8000/todos</code> endpoint. The image below gives you a clear explanation of what the <code>networkFirst</code> strategy implies. You always check the network first, and only in case of the network failing do you go to the cache to fetch the resource. This is a typical strategy you might use when querying an API that is likely to provide fresh data.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*xTrAJtPw5Cdb-SErJgBSYg.png" alt="Image" width="800" height="388" loading="lazy">
<em>Network First strategy</em></p>
<p>Now, the app is still not going to load because we are still missing two important pieces. Namely, we are still not caching</p>
<ol>
<li>The JS bundle that is being served by our local dev server.</li>
<li>The <code>index.html</code> file</li>
</ol>
<p>Add the following code to <code>custom-service-worker.js</code></p>
<pre><code>workbox.routing.registerRoute(
</code></pre><pre><code>  /\.(?:js|css|html)$/,
</code></pre><pre><code>  workbox.strategies.networkFirst(),
</code></pre><pre><code>)
</code></pre><pre><code>workbox.routing.registerRoute(
</code></pre><pre><code>  ‘http:<span class="hljs-comment">//localhost:3000',</span>
</code></pre><pre><code>  workbox.strategies.networkFirst()
</code></pre><pre><code>)
</code></pre><p>If you notice, the first route in the above code snippet is a <code>RegEx</code> object. This is a clean and simple way to target multiple routes with the same strategy. However, if you are targeting a resource that doesn’t follow the same origin policy, make sure to specify the entire route.</p>
<p>This is, of course, not the ideal way to do things. Ideally, we want static assets like JS bundles, stylesheets and HTML files pre-cached as part of the Webpack build process. We will get to that, but its important to understand that there is no black magic going on. This is all just simple caching.</p>
<p>Go ahead and fire up the page again and open up your console. You should see a bunch of logs by Workbox about routing. Go into offline mode, and refresh the page. You should see everything load just like normal. If you open up the workbox logs in the console, you will see Workbox printing out whether the network request failed or succeeded, and workbox’s response to that failure (see screenshot below):</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*deKoAAdcLbj8PjqykZNvsQ.png" alt="Image" width="587" height="369" loading="lazy">
<em>Workbox log in Chrome Dev Tools Window</em></p>
<h3 id="heading-implementing-deferred-posting-of-data-with-workbox"><strong>Implementing Deferred POSTing Of Data With Workbox</strong></h3>
<p>Alright, next up: how do we POST data back to the server without a network connection?</p>
<p>First, let’s set up a way to POST data back online, and make sure it works. Update your <code>addTodo</code> function inside of your Todo component so it looks like the following:</p>
<p>All we’ve done is added a callback handler to <code>setState</code> so we can be notified when the state has updated. At this point, we’ve made a POST request to the <code>json-server</code> to update <code>db.json</code> with the new todo.</p>
<p>Try submitting a new todo, open up <code>db.json</code> and you should see the new todo added to your array of objects.</p>
<p>Now, try doing the exact same thing offline, and you should get a network error for obvious reasons. You will probably get a log statement that says: Failed to fetch.</p>
<p>To solve this, we’re going to make use of something called backgroundSync, the spec for which you can read up on <a target="_blank" href="https://wicg.github.io/BackgroundSync/spec/">here</a>. The way its supposed to work is that whenever you make a request to a server for a specific resource (in our case a POST request), if no network is detected, Workbox will store this request in indexedDB and keep polling the request for a set period of time. When a network connection is detected, the request will be replayed. If no network connection is established within the pre-defined period of time, the request is discarded.</p>
<p>The backgroundSync API uses something called SyncManager under the hood. You can read about it in the MDN docs <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SyncManager">here</a>. Unfortunately, as you can see, SyncManager is not on the standards track and Chrome is the only browser that has a fully implemented spec. What this means is that Chrome is the only browser where this is guaranteed to work reliably.</p>
<p>We need to add some code to <code>custom-service-worker.js</code> to get the backgroundSync stuff working for us. Add the following code to the file:</p>
<p>We are making use of a background sync plugin that Workbox provides us with. The first parameter you provide to the constructor is the name of the queue you want Workbox to create when storing failed requests. The second parameter is an options object, where we are defining the maximum amount of time to attempt to replay requests within.</p>
<p>Finally, we register a new route with the POST method, and set up the strategy we want to use for caching. This is very similar to what we have already done with the exception of defining the type of request being made, and also having a plugin defined for our strategy.</p>
<p>Now, try running through the same scenario of submitting a todo without any network connection and observe what happens in the log. You will get a log that looks like the following screenshot.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fga8x8OIE4dT8b4DMvBRIw.png" alt="Image" width="800" height="117" loading="lazy">
<em>Workbox adds the failed request to a queue</em></p>
<p>You can look at the request that has been added by looking for indexedDB under the application tab in the Chrome DevTools window. Open up the listed subdirectories under the indexedDB dropdown menu, and you should see the request stored, waiting to be replayed.</p>
<p>Switch off the offline option in the DevTools window, and you should see a new Workbox log popup almost immediately. It will look like the following:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*C46elEi-gXvBXEwpy1INqw.png" alt="Image" width="800" height="120" loading="lazy">
<em>Workbox log detailing that the failed request has been replayed and submitted</em></p>
<p>The image above involves Workbox replaying the failed request the moment it receives a sync request, and giving you the confirmation that your request has been successful. If you look at <code>db.json</code> now, you will notice that the new todo has been added to the file.</p>
<p>Well, there we go. We have a way to replay failed requests through a service worker now.</p>
<p>What we need to do next is to integrate a Webpack plugin so Workbox can cache static assets as part of the build process. This will get rid of the need to explicitly have a route to cache static assets inside of our Service Worker file.</p>
<h3 id="heading-precaching-static-assets"><strong>Precaching Static Assets</strong></h3>
<p>This is going to be the final step. In this section, we are going to make the changes to CRA’s build process to force it to generate the Service Worker file using Workbox instead of <code>sw-precache</code>.</p>
<p>First up, install the following packages: <code>workbox-webpack-plugin</code> and <code>path</code>.</p>
<p>Open up the <code>package.json</code> file and edit the build script to run with <code>react-app-rewired</code> instead of <code>react-scripts</code> the same way we did for the start script.</p>
<p>Finally, open up the <code>config-overrides.js</code> file and edit it to look like the following:</p>
<p>There’s a couple of things we’re doing in this file.</p>
<p>First, we check to see if it’s a production build. If it is, we create a Workbox config object and provide it with the path of our custom SW, and also the path of the output SW we want.</p>
<p>We also provide an option called <code>importWorkboxFrom</code> and set it to <code>disabled</code>.</p>
<p>This is an option specifying that we don’t want Workbox imported from anywhere, since we’re directly requesting it from a CDN in our SW script.</p>
<p>Finally, we have a function that is called <code>removeSWPrecachePlugin</code> . All this does is loop over the plugins listed in the Webpack config, find the correct one, and return the index so we can remove it.</p>
<p>Now, go ahead and run the build for the app, and open up the SW file generated in the build folder. In my case, this SW file has the name <code>custom-service-worker.js</code></p>
<p>You will notice a new <code>importScripts</code> call at the top of the file, which seems to be requesting a precache manifest file. This file is stored in the build folder, and if you open it up, you should see the list of all static assets being cached by Workbox.</p>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>So, we’ve accomplished the following goals:</p>
<ol>
<li>Configure the CRA build to use <a target="_blank" href="https://github.com/timarney/react-app-rewired">react-app-rewired</a></li>
<li>Use react-app-rewired to customise the build to use Workbox to generate a Service Worker — We accomplished this using <code>workbox-webpack-plugin.</code> The build process will now automatically cache all static assets.</li>
<li>Build a very simple todo app</li>
<li>Implement offline functionality for the todo app using Workbox.<br>The offline functionality we will be targeting:<br>a) Cache retrieved assets so they can be served offline<br>b) Allow POSTing of data offline</li>
</ol>
<p>Here is the <a target="_blank" href="https://github.com/redixhumayun/react-app-rewired-workbox">link</a> to the repo which has a working version of the app. You can clone that and have a play with it.</p>
<blockquote>
<p>Follow me on twitter <a target="_blank" href="https://twitter.com/zz_humayun">here</a>. Follow me on GitHub <a target="_blank" href="https://github.com/redixhumayun">here</a></p>
</blockquote>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Progressive Web Apps 102: Building a Progressive Web App from scratch ]]>
                </title>
                <description>
                    <![CDATA[ We learnt about what is a Progressive Web App (PWA) in part 1. In this part, we are going to build a progressive web app using no frameworks but just DOM manipulation. Let’s do a quick recap of what we have learnt so far. For an app to be progressive... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/progressive-web-apps-102-building-a-progressive-web-app-from-scratch-397b72168040/</link>
                <guid isPermaLink="false">66b99cfd17d9592471979c5e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Shruti Kapoor ]]>
                </dc:creator>
                <pubDate>Thu, 13 Sep 2018 00:19:37 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*q57QiIkbThi9Mqvl" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>We learnt about <a target="_blank" href="https://medium.freecodecamp.org/progressive-web-apps-101-the-what-why-and-how-4aa5e9065ac2">what is a Progressive Web App (PWA)</a> in part 1. In this part, we are going to build a progressive web app using no frameworks but just DOM manipulation.</p>
<p>Let’s do a quick recap of what we have learnt so far. For an app to be progressive, it needs to have the following requirements:</p>
<ol>
<li>a manifest file — <code>manifest.json</code></li>
<li>service worker with at least a fetch event — <code>serviceworker.js</code></li>
<li>icon — <code>icon.jpeg</code></li>
<li>served over HTTPS — <code>https://www.myawesomesite.com</code></li>
</ol>
<p>In this tutorial, I will be talking about requirements 1 and 2 — creating a manifest file and registering a service worker.</p>
<h3 id="heading-objective">Objective</h3>
<p>For this example, we are going to create a simple progressive web app. The complexity is kept intentionally simple so that we can focus on the concepts of a progressive web app. You should be able to take these concepts and apply them in your own Angular, React, Vue or vanilla JavaScript app.</p>
<p>We are going to create a meme engine. We will pull the latest trending memes from <code>giphy.com</code> and display them in our app. A user should be able to view the images even if the connection is down. Hence, we are providing a seamless offline experience.</p>
<p>Great! So now let's get to the important stuff.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*6EJH5wIYnR3sHy6yI4bm7w.gif" alt="Image" width="480" height="270" loading="lazy"></p>
<h3 id="heading-step-0-build-the-app">Step 0: Build the app</h3>
<p>Let’s start with a skeleton index.html:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>All the memes!<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/styles.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"center"</span>&gt;</span>Top trending memes today<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>As you can see, it is a simple <code>index.html</code> that only prints out the text <code>Top trending memes today</code>. Nothing fancy.</p>
<p>Next, let’s add an ability to fetch trending memes from <code>giphy.com</code>. Here is what the fetch function looks like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchTrending</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://api.giphy.com/v1/gifs/trending?api_key=<span class="hljs-subst">${apiKey}</span>&amp;limit=25`</span>);
    <span class="hljs-keyword">const</span> json = <span class="hljs-keyword">await</span> res.json();

    main.innerHTML = json.data.map(createMeme).join(<span class="hljs-string">'\n'</span>);
}
</code></pre>
<h3 id="heading-lets-make-it-progressive">Let’s make it progressive</h3>
<h4 id="heading-step-1-manifest-file">Step 1: Manifest file</h4>
<p>As you may recall from part 1, the manifest file is a <code>json</code> file. It has meta information about the app like icon name, background color, the name of the app, etc. Here is a <code>manifest.json</code> file with these parameters:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Meme"</span>,
  <span class="hljs-attr">"short_name"</span>: <span class="hljs-string">"Meme"</span>,
  <span class="hljs-attr">"icons"</span>: [{
    <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/icons/icon-128x128.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"128x128"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    }, {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/icons/icon-144x144.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"144x144"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    }, {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/icons/icon-152x152.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"152x152"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    }, {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/icons/icon-192x192.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"192x192"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    }, {
      <span class="hljs-attr">"src"</span>: <span class="hljs-string">"images/icons/icon-256x256.png"</span>,
      <span class="hljs-attr">"sizes"</span>: <span class="hljs-string">"256x256"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"image/png"</span>
    }],
  <span class="hljs-attr">"start_url"</span>: <span class="hljs-string">"/index.html"</span>,
  <span class="hljs-attr">"display"</span>: <span class="hljs-string">"standalone"</span>,
  <span class="hljs-attr">"background_color"</span>: <span class="hljs-string">"#3E4EB8"</span>,
  <span class="hljs-attr">"theme_color"</span>: <span class="hljs-string">"#2F3BA2"</span>
}
</code></pre>
<p>You can also use a tool to generate this. <a target="_blank" href="https://app-manifest.firebaseapp.com/">Here is a tool</a> that I found useful:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EeVAMTLF9yowvPPJuOHpqw.png" alt="Image" width="800" height="276" loading="lazy">
<em>Web App manifest generator</em></p>
<p>Adding it to our app is simple. Add the following line to <code>index.html</code> :</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"manifest"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/manifest.json"</span>&gt;</span>
</code></pre>
<h4 id="heading-step-2-service-worker"><strong>Step 2: Service Worker</strong></h4>
<p>Let’s create the file <code>serviceworker.js</code> . First, we are going to register the service worker on install. Then we will cache some static assets such as <code>styles.css</code> and <code>app.js.</code> Next, we need to provide offline capability using <code>fetch</code> . Here is what the <code>serviceWorker.js</code> looks like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> staticAssets = [
    <span class="hljs-string">'./'</span>,
    <span class="hljs-string">'./styles.css'</span>,
    <span class="hljs-string">'./app.js'</span>
];

self.addEventListener(<span class="hljs-string">'install'</span>, <span class="hljs-keyword">async</span> event =&gt; {
    <span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">await</span> caches.open(<span class="hljs-string">'static-meme'</span>);
    cache.addAll(staticAssets);
});

self.addEventListener(<span class="hljs-string">'fetch'</span>, <span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> {request} = event;
    <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> URL(request.url);
    <span class="hljs-keyword">if</span>(url.origin === location.origin) {
        event.respondWith(cacheData(request));
    } <span class="hljs-keyword">else</span> {
        event.respondWith(networkFirst(request));
    }

});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cacheData</span>(<span class="hljs-params">request</span>) </span>{
    <span class="hljs-keyword">const</span> cachedResponse = <span class="hljs-keyword">await</span> caches.match(request);
    <span class="hljs-keyword">return</span> cachedResponse || fetch(request);
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">networkFirst</span>(<span class="hljs-params">request</span>) </span>{
    <span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">await</span> caches.open(<span class="hljs-string">'dynamic-meme'</span>);

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(request);
        cache.put(request, response.clone());
        <span class="hljs-keyword">return</span> response;
    } <span class="hljs-keyword">catch</span> (error){
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> cache.match(request);

    }

}
</code></pre>
<p>Let’s break this down. A service worker will help us cache data and fetch resources. If we have data in our cache, we return the data from cache or else fetch it from the network. For your own app, think of what functionality you will need to provide for offline access. Then, cache resources accordingly. For my case, I want to show previously cached images when the network is down.</p>
<p>We will need to add this to our index.html. To add it, we will register the service worker by leveraging the browser’s navigator library:</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, <span class="hljs-keyword">async</span> e =&gt; {
    <span class="hljs-keyword">await</span> fetchTrending();

    <span class="hljs-keyword">if</span> (<span class="hljs-string">'serviceWorker'</span> <span class="hljs-keyword">in</span> navigator) {
        <span class="hljs-keyword">try</span> {
            navigator.serviceWorker.register(<span class="hljs-string">'serviceWorker.js'</span>);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'SW registered'</span>);

        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'SW failed'</span>);

        }
    }
});
</code></pre>
<p>Let’s verify that it actually has been registered. Click on the network tab in the browser and go to application settings. This tab is really helpful when developing a progressive web app. Reload the page, and you will be able to see a service worker in this tab.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ayDNoz8Aw59BlVTfhrSU-w.png" alt="Image" width="800" height="500" loading="lazy">
<em>Service Worker has been registered</em></p>
<p>Now let’s refresh the browser. In the first load, data will be cached by the service worker. Try turning the connection off. We will still be able to view images.</p>
<p>Our app is now available even offline! If you have enabled HTTPS and uploaded an icon, congratulations you now have a Progressive Web App!</p>
<h3 id="heading-next-steps">Next Steps</h3>
<p>If you are interested in developing your own progressive web app, I would highly recommend checking out this <a target="_blank" href="https://codelabs.developers.google.com/codelabs/your-first-pwapp/">codelabs</a> by Google Developers.</p>
<p>Did you learn something new? Have comments? Know a DevJoke? <a target="_blank" href="https://twitter.com/shrutikapoor08">Tweet me @shrutikapoor08</a></p>
<p></p><blockquote><p>// When I wrote this, only God and I understood what I was doing<br>// Now, only God knows<a href="https://twitter.com/hashtag/devjoke?src=hash&amp;ref_src=twsrc%5Etfw">#devjoke</a> <a href="https://twitter.com/hashtag/notajoke?src=hash&amp;ref_src=twsrc%5Etfw">#notajoke</a> <a href="https://twitter.com/hashtag/development?src=hash&amp;ref_src=twsrc%5Etfw">#development</a> <a href="https://twitter.com/hashtag/javascript?src=hash&amp;ref_src=twsrc%5Etfw">#javascript</a> <a href="https://t.co/4V6lMUdhdb">pic.twitter.com/4V6lMUdhdb</a></p>— Shruti Kapoor (@shrutikapoor08) <a href="https://twitter.com/shrutikapoor08/status/1027666190447955968?ref_src=twsrc%5Etfw">August 9, 2018</a></blockquote><p></p>



 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to send notifications to your Web App using Python ]]>
                </title>
                <description>
                    <![CDATA[ By Lucas Hild Native apps have become hugely popular recently, mostly because of features such as working offline, transitions, easy distributability and of course push notifications. But unfortunately, you need a good knowledge of languages like Jav... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-send-notifications-to-your-web-app-using-python-ba490b893292/</link>
                <guid isPermaLink="false">66c3546153e0c377d44064d5</guid>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 08 Aug 2018 21:54:50 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*htDJgQOlkHDn7eF9WITh8Q.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Lucas Hild</p>
<p>Native apps have become hugely popular recently, mostly because of features such as working offline, transitions, easy distributability and of course push notifications. But unfortunately, you need a good knowledge of languages like Java or Swift to create a valuable native app.</p>
<h3 id="heading-progressive-web-apps">Progressive Web Apps</h3>
<p>Progressive Web Apps (PWAs) are JavaScript applications that run in the browser. They make the effort to bring some of the native app features to the web. PWAs are easy to develop if you have a fundamental knowledge of HTML, CSS, and in particular JavaScript. Moreover, if your service is already accessible for desktop devices on a website, it is easier to add the functionalities of a Web App, instead of developing a native mobile app.</p>
<h3 id="heading-notifications">Notifications</h3>
<p>Notifications keep users informed about new messages, tell them about a new blog post, and so on.</p>
<p>Many native apps send push notifications to the user. But this is also possible using PWAs and the Notifications API.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/H0tzewZI3D7d-DcwdysrJx3CpPTE2IcvEUeE" alt="Image" width="800" height="600" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@jamie452?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="_blank" title=""&gt;Jamie Street on &lt;a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-onesignal">OneSignal</h3>
<p>In this tutorial, we will be using <a target="_blank" href="https://onesignal.com">OneSingal</a> to send notifications to our web app. OneSignal is a powerful tool that provides a simple interface to push notifications. They also provide a Rest API, which we will be using to send notifications.</p>
<h3 id="heading-setup-onesignal">Setup OneSignal</h3>
<p>To send push notifications, you need to setup OneSignal first. Therefor you need an account on OneSignal. Head over to <a target="_blank" href="https://onesignal.com">their website</a> and press “Log in” in the upper right corner.</p>
<p>Next you will have to create an app. Give it a name and choose “Setup platform”. Here you select “All Browsers”. After that, you choose “custom code” as the integration. Then you have to provide some information about your website.</p>
<p>In the settings area of your app, there is a tab called “Keys &amp; IDs”. Copy both keys for later.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/CZolsxSB91fV86I3nzpJL2qHSLEOfSyR0eWz" alt="Image" width="800" height="413" loading="lazy"></p>
<p><strong>Important: Do not share you REST API Key. Keep it private!</strong></p>
<p>That’s it for setting up OneSignal. That was easy!</p>
<h3 id="heading-setup-our-website">Setup our website</h3>
<p>In the next part, we will be adding the notification functionality to our website. The website will have to wait for notifications sent by OneSignal and display them to the user.</p>
<p>To let the browser know that you are creating a Progressive Web App, we will add a file called <strong>manifest.json</strong> to the root of our project.</p>
<pre><code>{  <span class="hljs-string">"name"</span>: <span class="hljs-string">"My Application"</span>,  <span class="hljs-string">"short_name"</span>: <span class="hljs-string">"Application"</span>,  <span class="hljs-string">"start_url"</span>: <span class="hljs-string">"."</span>,  <span class="hljs-string">"display"</span>: <span class="hljs-string">"standalone"</span>,  <span class="hljs-string">"background_color"</span> : <span class="hljs-string">"#fff"</span> ,  <span class="hljs-string">"description"</span>: <span class="hljs-string">"We send notifications to you"</span>,  <span class="hljs-string">"gcm_sender_id"</span>: <span class="hljs-string">"482941778795"</span>,  <span class="hljs-string">"gcm_sender_id_comment"</span>: <span class="hljs-string">"Do not change the GCM Sender ID"</span>}
</code></pre><p>The first six key-value-pairs describe the appearance of the application. The <strong>gcm_send_id</strong> is important for sending notifications. If you want to know more about <strong>manifest.json</strong>, you can have a look in the <a target="_blank" href="https://developer.mozilla.org/de/docs/Web/Manifest">Mozilla Documentation</a>.</p>
<p>Your browser doesn’t automatically look for the manifest. You have to put the path to it in every HTML document in the _&amp;_lt;h_e_ad&gt; tag.</p>
<pre><code>&lt;head&gt;    ...    &lt;link rel=<span class="hljs-string">"manifest"</span> href=<span class="hljs-string">"manifest.json"</span>&gt;    ...&lt;/head&gt;
</code></pre><p>Additionally, we need some JavaScript code to connect our website to OneSignal.</p>
<p>You can put the code for that in a script tag in the _&amp;_lt;h_e_ad&gt; part. Don’t forget to re<strong>place my-</strong>app-id with your own OneSignal app id.</p>
<pre><code>&lt;head&gt;    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.onesignal.com/sdks/OneSignalSDK.js"</span> <span class="hljs-attr">async</span>=<span class="hljs-string">""</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">        <span class="hljs-keyword">var</span> OneSignal = <span class="hljs-built_in">window</span>.OneSignal || [];        OneSignal.push(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{            OneSignal.init({                <span class="hljs-attr">appId</span>: <span class="hljs-string">"my-app-id"</span>,                <span class="hljs-attr">autoRegister</span>: <span class="hljs-literal">false</span>,                <span class="hljs-attr">notifyButton</span>: {                    <span class="hljs-attr">enable</span>: <span class="hljs-literal">true</span>,                },            });        });    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span></span></span></span></span>
</code></pre><p><img src="https://cdn-media-1.freecodecamp.org/images/rXjHpdqOUr9ahuanYkm9FlHip7ftX12pFV-n" alt="Image" width="800" height="447" loading="lazy">
<em>Prompt the user to subscribe to your notifications</em></p>
<p>When you want to prompt the user to subscribe to your notifications, you execute this piece of code.</p>
<pre><code>OneSignal.push(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{    OneSignal.showHttpPrompt();});
</code></pre><p>Moreover, you need a service worker, which listens in the background for notifications. Therefore, you need two files in the root directory of your project.</p>
<p><strong>OneSignalSDKUpdaterWorker.js</strong></p>
<pre><code>importScripts(<span class="hljs-string">'https://cdn.onesignal.com/sdks/OneSignalSDKWorker.js'</span>);
</code></pre><p><strong>OneSignalSDKWorker.js</strong></p>
<pre><code>importScripts(<span class="hljs-string">'https://cdn.onesignal.com/sdks/OneSignalSDKWorker.js'</span>);
</code></pre><h3 id="heading-access-the-api-using-python">Access the API using Python</h3>
<p>OneSignal has an easy-to-use Rest API. The endpoints are documented in the <a target="_blank" href="https://documentation.onesignal.com/docs">OneSignal Developer Documentation</a>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/JonbuaZJXt9N2C-HIZzS-JR6gp1trvGEZ741" alt="Image" width="800" height="533" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@maxcodes?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="_blank" title=""&gt;Max Nelson on &lt;a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="<em>blank" title=")</em></p>
<p>To access it, we need to send HTTP requests. Therefore, we will use a library called <a target="_blank" href="http://docs.python-requests.org/en/master/"><strong>requests</strong></a>. To install it, you can use pip, the package manager of Python.</p>
<pre><code>pip install requests
</code></pre><p>This is the API endpoint we need to send a notification: <a target="_blank" href="https://onesignal.com/api/v1/notifications">https://onesignal.com/api/v1/notifications</a>.</p>
<p>The HTTP protocol has several methods. In this case, we want to make a POST request. To do so, we need to import requests and execute a function.</p>
<pre><code><span class="hljs-keyword">import</span> requests
</code></pre><pre><code>requests.post(<span class="hljs-string">"https://onesignal.com/api/v1/notifications"</span>)
</code></pre><p>OneSignal wants to verify that only you can send notifications to your website. So you have to add an HTTP header with your Rest API Key from OneSignal.</p>
<pre><code>requests.post(    <span class="hljs-string">"https://onesignal.com/api/v1/notifications"</span>,    headers={<span class="hljs-string">"Authorization"</span>: <span class="hljs-string">"Basic my-rest-api-key"</span>})
</code></pre><p>Remember to replace <strong>my-rest-api-key</strong> with your Rest API Key.</p>
<p>Moreover, you need some basic information about your notification.</p>
<pre><code>data = {    <span class="hljs-string">"app_id"</span>: <span class="hljs-string">"my-app-id"</span>,    <span class="hljs-string">"included_segments"</span>: [<span class="hljs-string">"All"</span>],    <span class="hljs-string">"contents"</span>: {<span class="hljs-string">"en"</span>: <span class="hljs-string">"Hello"</span>}}
</code></pre><pre><code>requests.post(    <span class="hljs-string">"https://onesignal.com/api/v1/notifications"</span>,    headers={<span class="hljs-string">"Authorization"</span>: <span class="hljs-string">"Basic my-rest-api-key"</span>},    json=data)
</code></pre><p>Replace <strong>my-app-id</strong> with your own app id. Next you choose who will receive your notifications. Example values are <code>"All", "Active Users", "Inactive Users”</code>. But you can also create your own segments. And for the last one, you add some content of the message in English. If you need another language, you can add it here too.</p>
<p><strong>That’s it! If you subscribed to the notifications, you should get a push notification.</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Z6q3cFDErQJTIstdDh8lc93gBpRgtvLxJmTs" alt="Image" width="800" height="588" loading="lazy"></p>
<h3 id="heading-send-notifications-using-an-api-wrapper">Send notifications using an API Wrapper</h3>
<p>Because my code became kind of messy with many different notifications, I <a target="_blank" href="https://github.com/Lanseuo/onesignal-notifications">created an API wrapper for OneSignal</a>.</p>
<h4 id="heading-api-wrapper">API Wrapper</h4>
<p>But what is an API wrapper? An API wrapper makes it easier for you to access an API. You can say that it is an API for an API. You call the API wrapper instead of the API directly.</p>
<p>You can install the wrapper called <strong>OneSignal-Notifications</strong> from pip.</p>
<pre><code>pip install onesignal-notifications
</code></pre><p>Now you can import it and setup your client.</p>
<pre><code><span class="hljs-keyword">from</span> onesignal <span class="hljs-keyword">import</span> OneSignal, SegmentNotificationclient = OneSignal(<span class="hljs-string">"MY_APP_ID"</span>, <span class="hljs-string">"MY_REST_API_KEY"</span>)
</code></pre><p>To send a Notification, you have to initialize the class <strong>SegmentNotification</strong> and use the method <strong>send</strong>.</p>
<pre><code>notification_to_all_users = SegmentNotification(    {        <span class="hljs-string">"en"</span>: <span class="hljs-string">"Hello from OneSignal-Notifications"</span>    },    included_segments=SegmentNotification.ALL)client.send(notification_to_all_users)
</code></pre><p>Maybe this looks kind of unnecessary to you, because it takes even more lines of code. But if you have several notifications, it makes the process much easier and your code more beautiful.</p>
<p>For example if you want to send a notification, which is based on some conditions, the API wrapper has a custom class for that.</p>
<pre><code><span class="hljs-keyword">from</span> onesignal <span class="hljs-keyword">import</span> OneSignal, FilterNotification, Filterclient = OneSignal(<span class="hljs-string">"MY_APP_ID"</span>, <span class="hljs-string">"MY_REST_API_KEY"</span>)
</code></pre><pre><code>filter_notification = FilterNotification(    {        <span class="hljs-string">"en"</span>: <span class="hljs-string">"Hello from OneSignal-Notifications"</span>    },    filters=[        Filter.Tag(<span class="hljs-string">"my_key"</span>, <span class="hljs-string">"&lt;"</span>, <span class="hljs-string">"5"</span>),        <span class="hljs-string">"AND"</span>,        Filter.AppVersion(<span class="hljs-string">"&gt;"</span>, <span class="hljs-string">"5"</span>),        <span class="hljs-string">"OR"</span>,        Filter.LastSession(<span class="hljs-string">"&gt;"</span>, <span class="hljs-string">"1"</span>),    ])
</code></pre><p>There are many custom parameters you can provide to adapt your notification. For example, you can add buttons to the notification. All list of all parameters can be found <a target="_blank" href="https://lanseuo.github.io/onesignal-notifications/guide/send-notification.html#common-parameters">here</a>.</p>
<pre><code><span class="hljs-keyword">from</span> onesignal <span class="hljs-keyword">import</span> OneSignal, FilterNotification, Filterclient = OneSignal(<span class="hljs-string">"MY_APP_ID"</span>, <span class="hljs-string">"MY_REST_API_KEY"</span>)
</code></pre><pre><code>filter_notification = SegmentNotification(    {        <span class="hljs-string">"en"</span>: <span class="hljs-string">"Hello from OneSignal-Notifications"</span>    },    web_buttons=[        {          <span class="hljs-string">"id"</span>: <span class="hljs-string">"like-button"</span>,          <span class="hljs-string">"text"</span>: <span class="hljs-string">"Like"</span>,          <span class="hljs-string">"icon"</span>: <span class="hljs-string">"http://i.imgur.com/N8SN8ZS.png"</span>,          <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://github.com/Lanseuo/onesignal-notifications"</span>}    ],    included_segments=SegmentNotification.ALL)
</code></pre><p>If you want to find out more about <strong>OneSignal-Notifications</strong>, you can have a look in the <a target="_blank" href="https://github.com/Lanseuo/onesignal-notifications">GitHub Repository</a> or in the <a target="_blank" href="https://lanseuo.github.io/onesignal-notifications">docs</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a PWA with Create-React-App and custom service workers ]]>
                </title>
                <description>
                    <![CDATA[ By Zaid Humayun Note: This is not a primer on create-react-app or what a service worker is. This post assumes prior knowledge of both. So, I recently had the opportunity to work on a React project which involved publishing the resulting web applicati... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-pwa-with-create-react-app-and-custom-service-workers-376bd1fdc6d3/</link>
                <guid isPermaLink="false">66d461d155db48792eed3fd5</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ progressive web app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webpack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 08 Aug 2018 16:29:02 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*ZFhS3HkqFBOeau1Amj4uBQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Zaid Humayun</p>
<p><strong>Note: This is not a primer on create-react-app or what a service worker is. This post assumes prior knowledge of both.</strong></p>
<p>So, I recently had the opportunity to work on a React project which involved publishing the resulting web application as a Progressive Web Application (PWA).</p>
<p>I realised what a struggle it is to get a PWA with custom routes configured inside of a Create React App (CRA) build. Hopefully this helps out someone stuck in a similar position.</p>
<h3 id="heading-pwas-in-create-react-app"><strong>PWAs in Create-React-App</strong></h3>
<p>How exactly do we get a PWA running inside our CRA shell?</p>
<p>Now, the CRA shell bundles a service worker by default. You should have noticed that in a basic CRA shell, inside of the <code>index.js</code> file, there is a call to <code>registerServiceWorker</code>:</p>
<pre><code class="lang-js"><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> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./index.css'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;
<span class="hljs-keyword">import</span> registerServiceWorker <span class="hljs-keyword">from</span> <span class="hljs-string">'./registerServiceWorker'</span>;

ReactDOM.render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>, <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>));
registerServiceWorker();
</code></pre>
<p>You can create a new CRA app and look inside the <code>registerServiceWorker</code> file.</p>
<p>It looks quite complex, but it is really just checking to see if the environment variables are set for a production build and if a <code>serviceWorker</code> is supported in the current browser.</p>
<p>If you run a build with the command <code>yarn build</code>, you can open up the build folder and check inside to see that a <code>service-worker.js</code> file has been generated. This is the default service worker file CRA generates for you.</p>
<p>The formatting of the file is inline ES5 JavaScript, which makes it a little hard to read. But you can dump it into any prettifier, and you should see a more legible file.</p>
<p>Looking into the above file should show you that it is simply creating a static cache with the following cache name: <code>sw-precache-v3-sw-precache-webpack-plugin-+(selg.registration ? self.registration.scope)</code>. It then caches all of your static files like <code>index.html</code> and your <code>js</code> and <code>css</code> files inside of that cache.</p>
<p>You should also see a <code>fetch</code> event listener in there that catches a fetch event and checks to see if the app is requesting one of the previously cached static assets.</p>
<p>Now comes the million dollar question: what if you want to configure a dynamic cache for a specific route? In essence, a cache that will update itself with data sent from the server when the user visits a specified route. Note that this means the data will not be available at build time, and therefore cannot be cached by the default service worker generated.</p>
<h3 id="heading-limitations-of-default-pwas-in-cra"><strong>Limitations of default PWAs in CRA</strong></h3>
<p>Unfortunately, it’s not very easy to accomplish the above when using CRA. Unless you’re willing to <code>eject</code>, of course.</p>
<p>Take a look at these GitHub issues to see why the team at CRA won’t support customising the default service worker.</p>
<p><a target="_blank" href="https://github.com/facebook/create-react-app/issues/2237"><strong>Custom ServiceWorker config · Issue #2237 · facebook/create-react-app</strong></a><br><a target="_blank" href="https://github.com/facebook/create-react-app/issues/2237">_1.0.0 added Progressive Web App support, is it possible to support custom config in near future? I really don't want to…_github.com</a><a target="_blank" href="https://github.com/facebook/create-react-app/pull/2714"><strong>Import scripts in Service Worker by piotr-cz · Pull Request #2714 · facebook/create-react-app</strong></a><br><a target="_blank" href="https://github.com/facebook/create-react-app/pull/2714">_This PR adds an ability to use importScripts option of SWPrecacheWebpackPlugin. How-to: create a file called…_github.com</a></p>
<p>So, given that we can’t seem to customise the default service-worker, how do we work our way around it?</p>
<h3 id="heading-understanding-how-cra-generates-a-service-worker"><strong>Understanding How CRA Generates A Service Worker</strong></h3>
<p>The first step to finding a work around for the build system is to actually understand how the build system works.</p>
<p>So, let’s start with the <a target="_blank" href="https://github.com/GoogleChromeLabs/sw-precache">library</a> the build system uses to generate a service worker file.</p>
<p><code>sw-precache</code> is a library that allows you to generate a service worker file based on a template. The template file is written using underscore’s templating engine.</p>
<p><a target="_blank" href="https://github.com/GoogleChromeLabs/sw-precache/blob/master/service-worker.tmpl">Here</a> is the link to the template file in the <code>sw-precache</code> source code.</p>
<p>Again, the template file looks complex, but it is quite straightforward once you manage to get your head around the templating language.</p>
<p>So, what happens when you run a build process in a CRA shell, in relation to generating a service worker, is:</p>
<ol>
<li>The <code>sw-precache</code> library is executed internally</li>
<li>An options object is provided to <code>sw-precache</code> to allow generation of the service worker file from the template</li>
<li>The service worker file is generated in the <code>build</code> folder with the name <code>service-worker.js</code></li>
</ol>
<h3 id="heading-overriding-the-default-service-worker"><strong>Overriding The Default Service Worker</strong></h3>
<p>Now, how do we override the above process to allow our own custom service worker file to be generated?</p>
<p>The answer is based on Jeff Posnick’s (a maintainer of <code>sw-precache</code>) <a target="_blank" href="https://stackoverflow.com/questions/47636757/add-more-service-worker-functionality-with-create-react-app?rq=1">stackoverflow answer</a>.</p>
<p>First, we need to run the<code>sw-precache</code> CLI after the normal build process.</p>
<p>Install the <code>sw-precache</code> library by running the following command: <code>npm install --save-dev sw-precache</code></p>
<p>Now, the <code>sw-precache</code> library runs on a config file, which is provided via an option on the CLI. This is the command: <code>sw-precache --config=sw-precache-config.js</code> , where <code>sw-precache-config.js</code> is the name of the config file.</p>
<p>Here is a sample config file.</p>
<pre><code><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">staticFileGlobs</span>: [
    <span class="hljs-string">'build/static/css/**.css'</span>,
    <span class="hljs-string">'build/static/js/**.js'</span>
  ],
  <span class="hljs-attr">swFilePath</span>: <span class="hljs-string">'./build/service-worker.js'</span>,
  <span class="hljs-attr">templateFilePath</span>: <span class="hljs-string">'./service-worker.tmpl'</span>,
  <span class="hljs-attr">stripPrefix</span>: <span class="hljs-string">'build/'</span>,
  <span class="hljs-attr">handleFetch</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">runtimeCaching</span>: [{
    <span class="hljs-attr">urlPattern</span>: <span class="hljs-regexp">/this\\.is\\.a\\.regex/</span>,
    handler: <span class="hljs-string">'networkFirst'</span>
  }]
}
</code></pre><p><strong>Note:</strong> It is important that you specify the swFilePath as <code>./build/service-worker.js</code> This is so that the service worker generated as a result of your custom build process overwrites the one created by CRA (they both share the same name, in this instance). Otherwise, you will end up with two service worker files in your build directory!</p>
<p>There is extensive documentation on the object properties and what valid values can be assigned to them on the <a target="_blank" href="https://github.com/GoogleChromeLabs/sw-precache">GitHub page</a> for <code>sw-precache</code>.</p>
<p>Of special interest is the runtimeCaching option, because this is a very extensible solution to allow you to define custom rules for your service worker to respond to dynamic content.</p>
<p>The templateFilePath is an option for when you want the CLI to pick up your custom service worker template file. But you’re almost always going to be using the template file provided by the library itself.</p>
<p>Finally, we need to provide the script to signal to the CRA build system that we want our custom service worker to be generated. Go ahead and install the <code>sw-precache</code> library.</p>
<p>Next, update the package.json build script, with the following:</p>
<p><code>build: react-scripts build &amp;&amp; sw-precache --config=sw-precache-config.js</code></p>
<p>Once you run the build process with <code>npm run build</code>, you can open up the build folder and see your generated service worker.</p>
<p>Run the build process with and without the custom service worker and notice the differences between the two.</p>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>While this may seem like a very verbose approach to something as simple as customising a service worker, this approach has the benefit of keeping you firmly within the create-react-app shell.</p>
<p>There are other approaches to generating a custom service worker (using a combination of <a target="_blank" href="https://github.com/timarney/react-app-rewired">react-app-rewire</a> and <a target="_blank" href="https://github.com/GoogleChrome/workbox">workbox</a>). I’ll try and get that approach up in a post as well.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
