<?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[ Netlify - 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[ Netlify - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 05:06:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/netlify/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Automate Branch-Specific Netlify Configurations with a Bash Script: A Step-by-Step Guide ]]>
                </title>
                <description>
                    <![CDATA[ When you’re working on a project with multiple environments – like staging and production – for your backend APIs and frontend deployments, you’ll want to make sure you have the correct configuration and commands for each branch in your repository. T... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-automate-branch-specific-netlify-configurations-with-a-bash-script/</link>
                <guid isPermaLink="false">6760688b00f110abd3d0f655</guid>
                
                    <category>
                        <![CDATA[ Continuous Integration ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Bash ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Francis Ihejirika ]]>
                </dc:creator>
                <pubDate>Mon, 16 Dec 2024 17:51:07 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733871988108/cde4ea9b-705c-40e0-9730-09dbeebdfbae.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re working on a project with multiple environments – like staging and production – for your backend APIs and frontend deployments, you’ll want to make sure you have the correct configuration and commands for each branch in your repository.</p>
<p>This can be daunting in situations where multiple developers are actively working on a codebase, making changes to different branches, or managing multiple branch-specific configurations.</p>
<p>Like with every pull request or change pushed to a branch, you’ll need to review every line of code that’s been changed, added, or removed before deciding what gets merged or not. Configuration files in codebases are not exempt from this, making them prone to errors, as a simple change can affect your entire Continuous Integration setup.</p>
<p>When changes get made to the staging or production branch and a build is triggered, you’ll want to ensure that the correct resources attached to a branch are maintained. In some cases, you may need to define different redirect rules for each respective client, custom build commands, or other settings for each branch.</p>
<p>In this article, I’ll walk through how to manage branch-specific configurations including redirects for multiple branches automatically, using a simple bash script. I’ll also show you how to safely merge context-specific rules for your staging and production branches on Netlify.</p>
<h2 id="heading-what-well-cover">What we’ll cover:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-structure-and-scenario">Project Structure and Scenario</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-redirectsrewrites">What are Redirects/Rewrites?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-netlify-processes-redirects">How Netlify Processes Redirects</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-using-the-redirects-file-syntax">Using the _redirects file syntax</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-the-netlifytoml-configuration-file-syntax">Using the netlify.toml configuration file syntax</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-problem-managing-multiple-netlifytoml-files-for-different-branches">The Problem: Managing Multiple netlify.toml Files for Different Branches</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-the-script-to-automatically-create-our-configuration-files">How to Write the Script to Automatically Create Our Configuration File(s)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-sample-netlifytoml-file">Sample Netlify.toml file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-create-the-script-folder-and-add-the-script-file">Step 1: Create the script folder and add the script file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-modify-packagejson-to-include-the-script-command">Step 2: Modify package.json to include the script command</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deploy-our-client-to-netlify">How to Deploy Our Client to Netlify</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-first-deployment-of-your-project-to-netlify">First deployment of your project to Netlify</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-subsequent-deployments-how-to-set-up-branch-deployments">Subsequent Deployments / How to Set Up Branch Deployments</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-set-up-environment-variables-in-netlify-for-each-branch-context-production-staging-and-so-on">Step 1: Set Up Environment Variables in Netlify for each branch context — production, staging, and so on</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-trigger-a-new-deploy">Step 2: Trigger a new deploy</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-inspect-your-deployments">Inspect Your Deployments</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-project-structure-and-scenario">Project Structure and Scenario</h2>
<p>Consider a situation where you have two separate servers deployed for a project: one to serve requests to a staging environment (deployed to Render), and another to the production environment (deployed to Google Cloud Run).</p>
<p>You also have two separate client deployments on Netlify, each with their respective API_BASE_URLs, that are served by their respective servers as illustrated below:</p>
<p><img src="https://cdn-images-1.medium.com/max/1200/1*Zat3jiq5BCucEzDHKp8yuA.png" alt="Illustration showing branches of a project repository - development, staging and production - each with its own server and client" width="600" height="400" loading="lazy"></p>
<p>The image below is a <code>sample-project</code> repository, with <code>api</code> and <code>client</code> folders/directories within it. This is an overview of the structure in each of the branches outlined above. Each directory contains its own <code>package.json</code> file, is treated as an independent component, and can be deployed to two separate services.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*Vkh8EyIA5qamhoJOz2ksSg.png" alt="A project structure for a sample project, including directories and files for both backend and frontend. " width="600" height="400" loading="lazy"></p>
<p>In your frontend deployment for each of the clients, all your requests made to endpoints that begin with <code>/api/v1/</code> are routed to the server. Other routes remain within the frontend to direct you to pages within the client. So you’re required to write the correct rules to guide your client on how to process these requests. These are called redirect rules or rewrites.</p>
<h2 id="heading-what-are-redirectsrewrites">What are Redirects/Rewrites?</h2>
<p>Redirects, or rewrites, are rules you can create to have certain URLs automatically go to a new location anywhere on the internet (source: <a target="_blank" href="https://wpengine.com/">WPengine</a>). These are also generally known as <strong>URL forwarding</strong> and you can use them anywhere – in entire websites, sections of a website, or an entire web application.</p>
<p>In web applications, redirects are often utilized to determine how to process requests. Web hosting platforms such as Netlify and Vercel use them as well, giving developers the option to determine how their web applications process requests.</p>
<h2 id="heading-how-netlify-processes-redirects">How Netlify Processes Redirects</h2>
<p>Netlify has two possible ways to specify redirect rules. You can do it using the <code>_redirects</code> file syntax or using the <code>netlify.toml</code> configuration file syntax. They achieve the same goal, but the <code>netlify.toml</code> syntax gives you more options and capabilities.</p>
<h3 id="heading-using-the-redirects-file-syntax">Using the <code>_redirects</code> file syntax</h3>
<p>If you opt to use the redirect syntax, you should simply create a <code>_redirects</code> file in the public folder of your client app, and specify the redirect rules within it. That’s as easy as it gets. Below is an example of a redirect rule within the file:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733944577546/2f21a9b9-6843-4900-a6fe-5573a087b3d9.png" alt="Sample Netlify _redirects file showing usage syntax and redirect rules" class="image--center mx-auto" width="745" height="143" loading="lazy"></p>
<p>The above rule can be interpreted as:</p>
<ol>
<li><p>Send all my requests that match <code>/api/v1</code> to the API URL specified, and return a 200 success status code. The asterisks (*) after <code>/api/v1/</code> as seen in <code>/api/v1/*</code> instruct it to append any other extension of the original URL to the stated API URL. For example, if you have a <code>/api/v1/users</code> route in your frontend, that request will be redirected to <code>https://your-api-base-url.com/api/v1/users</code>. The <code>:splat</code> seen in the API URL is simply a placeholder.</p>
</li>
<li><p>Serve all other default routes through the index.html folder. This is required to ensure that you don’t encounter broken pages when you navigate to other pages and attempt to visit the previous page using the “back” button.</p>
</li>
</ol>
<h3 id="heading-using-the-netlifytoml-configuration-file-syntax">Using the <code>netlify.toml</code> configuration file syntax</h3>
<p>The <code>netlify.toml</code> configuration file gives you a lot more flexibility when specifying redirect rules, including but not limited to matching the original request route, the required destination, the preferred status code response, header rules, signatures, country restrictions, roles and more.</p>
<p>This is a sample <code>netlify.toml</code> file sourced from <a target="_blank" href="https://docs.netlify.com/routing/redirects/#syntax-for-the-netlify-configuration-file">Netlify’s documentation</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733947216566/f64670b4-9d28-4c50-a753-1deb27dfc646.png" alt="Sample netlify.toml file showing configuration" class="image--center mx-auto" width="717" height="488" loading="lazy"></p>
<p><strong>Quick Note:</strong> using the redirects file for redirecting certain requests to our API is perfectly fine. But it can be considered a security risk adding our API URL in plain text in the <em>redirects</em> file if the API_BASE_URL is supposed to be private. This is because any file in the public folder is what it sounds like – public – and anyone can access it.</p>
<p>If the direct locations you desire to have in your app are public URLs, then feel free to utilize the <code>_redirects</code> file syntax. But if you prefer to have a private URL(s), utilizing a <code>netlify.toml</code> configuration file in combination with the environment variables is generally a better idea.</p>
<h2 id="heading-the-problem-managing-multiple-netlifytoml-files-for-different-branches">The Problem: Managing Multiple <code>netlify.toml</code> Files for Different Branches</h2>
<p>When you use the <code>netlify.toml</code> file to define your build commands and environment-specific settings, and you make changes that are pushed to your repository and open pull requests, you have to manually ignore or edit each <code>netlify.toml</code>in each branch. This eventually becomes very stressful and susceptible to errors.</p>
<p>In addition to this, we want to avoid having our API URLs hardcoded in either our <code>_redirects</code> or <code>netlify.toml</code>file within our project codebase for security reasons. We will use environment variables as provided within our Netlify UI for production and staging contexts.</p>
<p>To avoid the above problems, we will use a small script in our codebase to dynamically generate the correct <code>netlify.toml</code> files for each branch. This approach eliminates conflicts and removes the need for manual intervention when switching between branches or handling pull requests.</p>
<h2 id="heading-how-to-write-the-script-to-automatically-create-our-configuration-files">How to Write the Script to Automatically Create Our Configuration File(s)</h2>
<h3 id="heading-sample-netlifytoml-file">Sample <code>Netlify.toml</code> file</h3>
<p>Below is a screenshot of a sample <code>netlify.toml</code> file we are trying to achieve for each build. You can see that all our requests that match <code>api/v1/</code> in our codebase will be routed to our API.</p>
<p>You could have your API endpoint requests structured differently, for example <code>/api/your-endpoint</code> – just make sure to adjust the script accordingly. In this sample project, we use <code>api/v1/your-endpoint</code> as our structure.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*oj_oJDA7lnC9we2zuQHm4w.png" alt="Netlify configuration file showing build commands and redirect rules" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-1-create-the-script-folder-and-add-the-script-file">Step 1: Create the script folder and add the script file</h3>
<p>In the <code>client</code> directory, create a <code>scripts/</code> directory and a <a target="_blank" href="http://configure-netlify.sh"><code>configure-netlify.sh</code></a> script file. You are required to do this for each branch in your repo. The content remains the same.</p>
<p>Open the <a target="_blank" href="http://configure-netlify.sh"><code>configure-netlify.sh</code></a> script file and paste the following content within it:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Ensure API_BASE_URL is set</span>
<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$API_BASE_URL</span>"</span> ]; <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Error: API_BASE_URL environment variable is not set."</span>
  <span class="hljs-built_in">exit</span> 1  <span class="hljs-comment"># Exit the script to stop the deployment</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"Using API endpoint: <span class="hljs-variable">$API_BASE_URL</span>"</span>

<span class="hljs-comment"># Define the desired Netlify configuration</span>
NETLIFY_CONFIG=<span class="hljs-string">"
[build]
  command = \"npm install &amp;&amp; npm run build\"
  base = \"client\"
  publish = \"dist\"

[[redirects]]
  from = \"/api/v1/*\"
  to = \"<span class="hljs-variable">$API_BASE_URL</span>/:splat\"
  status = 200
  force = true

[[redirects]]
  from = \"/*\"
  to = \"/index.html\"
  status = 200
"</span>

<span class="hljs-comment"># Create or update the netlify.toml file</span>
<span class="hljs-keyword">if</span> [ ! -f <span class="hljs-string">"netlify.toml"</span> ]; <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Creating netlify.toml file..."</span>
<span class="hljs-keyword">else</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Updating existing netlify.toml file..."</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$NETLIFY_CONFIG</span>"</span> &gt; netlify.toml

<span class="hljs-comment"># Confirm successful configuration</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"netlify.toml file has been configured successfully!"</span>
</code></pre>
<p>The script does the following:</p>
<ol>
<li><p>It checks the environment variables to ensure that the <code>API_BASE_URL</code> is set. If this isn’t set, it exits the build and causes it to fail. We did this to ensure that you don’t mistakenly create a successful deployment but with invalid URLs in production.</p>
</li>
<li><p>It then creates the content of the <code>netlify.toml</code> file as shown in the sample above. If your API endpoints use a different structure from <code>api/v1/your-endpoint</code>, you can adjust the script to fit your desired structure.</p>
</li>
<li><p>It checks if there already exists a <code>netlify.toml</code> file. If it doesn’t exist, it creates one and writes the content into it. If it exists, it updates it with the correct content during the build, using the <code>API_BASE_URL</code> set in the environment variables.</p>
</li>
</ol>
<h3 id="heading-step-2-modify-packagejson-to-include-the-script-command">Step 2: Modify <code>package.json</code> to include the script command</h3>
<p>To integrate this script with your build process, we will add a script command to the <code>package.json</code> file to call this script before running the actual build.</p>
<p>Add the <code>configure-netlify</code> command as follows: <code>"configure-netlify": "bash scripts/</code><a target="_blank" href="http://configure-netlify.sh"><code>configure-netlify.sh"</code></a> within the scripts in your <code>package.json</code> file.</p>
<p>Update your build command to run the script before running the actual build: <code>"build": "npm run configure-netlify &amp;&amp; vite build"</code>.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*Sds0AS4Poe80pc9D9YkBvQ.png" alt="Image showing updated package.json file with custom configure-netlify command and updated build command" width="600" height="400" loading="lazy"></p>
<p>Don’t forget to save these changes and push them to your remote repository.</p>
<h2 id="heading-how-to-deploy-our-client-to-netlify">How to Deploy Our Client To Netlify</h2>
<p>When deploying our client to Netlify, we are given three options:</p>
<ol>
<li><p>importing an existing project (that is, a project that exists on a git repository service such as GitHub and GitLab),</p>
</li>
<li><p>importing from a template, or</p>
</li>
<li><p>manually deploying a static site using the Netlify drop (drag and drop) interface.</p>
</li>
</ol>
<p>For the configuration in our repository to work as expected during our build process, you’ll need to use the option that requires importing from an existing project such as GitHub. Using the drag-and-drop interface won’t work. If you must use this, then opt for the <code>_redirects</code> file syntax option to define your redirects.</p>
<h3 id="heading-first-deployment-of-your-project-to-netlify">First deployment of your project to Netlify</h3>
<p>When deploying your project for the first time, you are given the option of deploying only one branch initially. You can only add and specify other options, such as other branches, in subsequent deployments.</p>
<p>To deploy your project, take the following steps:</p>
<ol>
<li><p>Sign in to Netlify &gt; <a target="_blank" href="http://netlify.com">netlify.com</a></p>
</li>
<li><p>Click "Add new site" &gt; "Import an existing project" &gt; "Deploy with GitHub"</p>
</li>
<li><p>Click "Configure Netlify on GitHub" &gt; Search for your repository &gt; Select it</p>
</li>
<li><p>Enter a unique site name for your project</p>
</li>
<li><p>Configure deploy settings. Here you are required to select the preferred branch to deploy. For the initial deployment, we will deploy the <code>main</code> branch which we use as the production branch.</p>
<ul>
<li><p>Branch: main/master</p>
</li>
<li><p>Build command: <code>npm run build</code></p>
</li>
<li><p>Publish directory: <code>dist</code> (Select the directory where your static file lives. In this sample project, it’s exported into the <code>dist</code> directory. Some tools export into <code>build</code>)</p>
</li>
</ul>
</li>
<li><p>Enter the environment variables for your project. Don’t forget to enter your <code>API_BASE_URL</code> from your server. This is an essential requirement according to the bash script.</p>
</li>
<li><p>Click "Deploy site"</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733951997499/f329f2e6-b977-4b1f-a6ea-6b20610dc0d2.png" alt="Netlify deployment screen showing optional project build settings" class="image--center mx-auto" width="1919" height="934" loading="lazy"></p>
<p>Your project should deploy correctly, and you’ll be able to see the <code>netlify.toml</code> configuration generated by the script by inspecting the deployment details at the bottom of the successful deployment page.</p>
<p>You can download this file to your local machine to see the configuration generated. It should match the sample <code>netlify.toml</code> file above. You can also test that it works using your generated site link.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733952720930/97ccee2f-e93b-4205-94fa-a8ab32dd37c2.png" alt="Netlify screen showing deploy log and static files after successful deployment" class="image--center mx-auto" width="1919" height="934" loading="lazy"></p>
<h2 id="heading-subsequent-deployments-how-to-set-up-branch-deployments">Subsequent Deployments / How to Set Up Branch Deployments</h2>
<h3 id="heading-step-1-set-up-environment-variables-in-netlify-for-each-branch-context-production-staging-and-so-on">Step 1: Set Up Environment Variables in Netlify for each branch context  — production, staging, and so on</h3>
<p>When your project has been deployed successfully, you can set up deployments for your staging branch. To edit the configurations, you’ll need to:</p>
<ol>
<li><p>Navigate to the list of your sites</p>
</li>
<li><p>Select your successfully deployed site</p>
</li>
<li><p>Click on “site configuration” on the left menu</p>
</li>
<li><p>Select “environment variables” &gt; click the “Add a variable” button.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733953093253/26948920-70a7-47bc-8f53-4cb19a9d8543.png" alt="Netlify site configuration page of successfully deployed site, showing environment variables" class="image--center mx-auto" width="1919" height="934" loading="lazy"></p>
<p>You will be given the option of adding variables individually or importing an entire <code>.env</code> file. You can choose either option. In the image below, I’ve selected “Import from a <code>.env</code> file”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734124727631/1bb20e6b-1232-4a79-bc18-2df2440cb641.png" alt="Environment variables screen showing options available to add a variable - using a single variable entry or multi entry from a .env file" class="image--center mx-auto" width="1919" height="367" loading="lazy"></p>
<p>Seeing that our production site, deployed from the <code>main</code> branch (with the production environmental variables), has already been deployed, you’ll need to:</p>
<ol>
<li><p>Uncheck the production branch (to prevent replacing the initially deployed main branch. Be careful not to mix up your environment variables for different branches)</p>
</li>
<li><p>Select “branch deploys”</p>
</li>
<li><p>Copy and paste the content of your .env file in the input section</p>
</li>
<li><p>Don’t forget to add the <code>API_BASE_URL</code> environment variable for your staging environment</p>
</li>
</ol>
<p>Note that in selecting branch deploys, the environment variables imported here will affect all branch deploys, apart from the production branch. You can further customize your contexts by selecting a custom branch, but that’s an entirely different approach which may require you to further customize your <code>netlify.toml</code> configuration file or the bash script.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733953419262/2f62d3d6-2549-4a35-aa0b-7a02225bd630.png" alt="Environment variables screen with available contexts for deployment" class="image--center mx-auto" width="1919" height="934" loading="lazy"></p>
<p>If you decide to import each environment variable individually, you are given a similar option as seen below. Ensure that you select the correct context for each branch.</p>
<p>DON’T USE THE SAME VALUES FOR ALL CONTEXTS. As seen in the image below, selecting “<em>different value for each deploy context</em>” will allow you to define the values for each one. In this case, we define the values for branch deploys. Your initially used production variable should already exist.</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*699HAdcahAzATFCDYlbqpw.png" alt="Environment variable insertion screen for single variable, showing value options for different contexts" width="600" height="400" loading="lazy"></p>
<p>When all the variables have been imported, you can inspect them to confirm that they have been correctly imported by selecting the dropdown on the right beside each variable and inspecting their values.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733954618431/66e90f42-4e3d-4c5b-95ec-a6ae03207498.png" alt="Environmental variables set for deployed web application" class="image--center mx-auto" width="1081" height="729" loading="lazy"></p>
<h3 id="heading-step-2-trigger-a-new-deploy">Step 2: Trigger a new deploy</h3>
<p>When all your environment variables have been imported for the different contexts – production and staging in this case – navigate to “deploys” on the left panel of your screen. Then hit the “Trigger deploy” button, clear the cache, and initiate a new deployment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733954838853/79685cf6-54a5-4495-8777-914fcc46950f.png" alt="79685cf6-54a5-4495-8777-914fcc46950f" class="image--center mx-auto" width="1916" height="500" loading="lazy"></p>
<h2 id="heading-inspect-your-deployments">Inspect Your Deployments</h2>
<p>You can confirm that your script works as expected by selecting any of the deployments and selecting the building dropdown in the “Deploy log”. You will see the command run, as well as your output and API URL for that deployment, as defined by your context.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733955355311/8268c1f3-c9cb-4b98-8094-59b7dd2d5b13.png" alt="Deploy log for successfully deployed web application, showing values logged by automation script during build" class="image--center mx-auto" width="1301" height="1005" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By following the steps in this guide, and using your script and updated commands in each branch in your repo, when you push changes then Netlify will automatically generate or update the <code>netlify.toml</code>file in each branch. This ensures that the correct configurations and environment variables for each environment are used at build time.</p>
<p>Your script remains the same across all the branches. This lets you focus on other code changes while your script handles the correct configuration for you safely and easily.</p>
<p>Push changes to any branch to see this in action.</p>
<p>Feel free to connect with me on <a target="_blank" href="https://x.com/@francisihej">Twitter</a> (@francisihej) or <a target="_blank" href="https://linkedin.com/in/francis-ihejirika">LinkedIn</a>!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Manually Deploy a React and TypeScript Project on Netlify ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, I'll teach you how to manually deploy a React and TypeScript project with Vite on Netlify. I will show you a few quick and simple steps to get live versions of your projects up and running. To follow along with this tutorial, there ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/manually-deploy-a-react-and-typescript-project-on-netlify/</link>
                <guid isPermaLink="false">66ba5226e727ef4561965aa7</guid>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Losalini Rokocakau ]]>
                </dc:creator>
                <pubDate>Fri, 12 Apr 2024 18:02:59 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/cover-image-4.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, I'll teach you how to manually deploy a React and TypeScript project with Vite on Netlify. I will show you a few quick and simple steps to get live versions of your projects up and running.</p>
<p>To follow along with this tutorial, there are a few prerequisites:</p>
<ol>
<li>An existing React and TypeScript project with Vite that you want to deploy.</li>
<li>Visual Studio Code (VSCode) editor or any other code editor such as Sublime.</li>
<li>Optionally, you can just use a command line/terminal instead of your code editor's built-in terminal.</li>
<li>An existing Netlify account that you've already logged into.</li>
</ol>
<p>Let's go ahead with our deployment!</p>
<h3 id="heading-what-to-expect">What to expect</h3>
<p>You will deploy your project manually on Netlify, rename your site on the platform, and be able to have a live version of your project.</p>
<h2 id="heading-how-to-deploy-your-react-and-typescript-project">How to Deploy Your React and TypeScript Project</h2>
<h3 id="heading-1-open-up-your-existing-project-on-vscode">1. Open up your existing project on VSCode</h3>
<p>Open up your project in the code editor of your choice.</p>
<h3 id="heading-2-build-the-project">2. Build the project</h3>
<p>Open the terminal in VSCode and use the code below to build your project:</p>
<pre><code>npm run build
</code></pre><p>This should generate a <em>dist</em> folder in your root directory where your projects' minified version is created and stored.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-2-0.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 2.0</strong> Running the project in the terminal which creates the dist folder in the project structure.</em></p>
<h3 id="heading-3-drag-and-drop-your-dist-folder-in-netlify">3. Drag and Drop your Dist Folder in Netlify</h3>
<p>On Netlify under Team Overview, click on the Add New Site button. It should have three options:</p>
<ol>
<li>Import an existing project</li>
<li>Start from a template</li>
<li>Deploy manually</li>
</ol>
<p>Select the third choice to deploy manually.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-3-0.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 3.0</strong> Clicking on the Add new site button shows the three options to choose from. Select "Deploy Manually".</em></p>
<p>It should lead you to the page as seen in the below image:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-3-1.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 3.1</strong> Drag &amp; drop view when choosing to deploy manually.</em></p>
<p>Click on the page and navigate into your project's file path.</p>
<p>Upload the dist folder that was previously generated in your project onto Netlify.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-3-2.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 3.2</strong> Navigating into the project's folder &amp; uploading the dist folder onto Netlify.</em></p>
<p>It will take a few seconds or minutes to get your project deployed.</p>
<p>Once deployment is successful, you will see a page similar to the one below. By default, it gives a randomly generated subdomain name to your site's URL. For example, in my case, it is <em>delightful-pie-bba293.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-3-3.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 3.3</strong> Deployment is successful.</em></p>
<p>Your site's name is the subdomain of Netlify's domain.</p>
<pre><code>https:<span class="hljs-comment">//&lt;your-site-name&gt;.netlify.app</span>
</code></pre><h3 id="heading-4-rename-your-site">4. Rename your site</h3>
<p>Click on the Site Configuration that is on the lefthand sidebar.</p>
<p>In the Site Information under Site Details, click on the Change Site Name button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-4-0.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 4.0</strong> Viewing the Site Details under the Site Configuration.</em></p>
<p>This should give you a pop-up to change your site's name.</p>
<p>Change the name of your site to whatever you like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-4-1-1.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 4.1</strong> Changing the name of your site.</em></p>
<p>Head back to the Site Overview where you can find the URL of your site. Your site's URL is found right underneath your project's name.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/figure-4-2.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 4.2</strong> Site Overview where you can find your site's URL.</em></p>
<p>Click on the URL to view your site!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/fig-4-3-1.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 4.3</strong> Viewing my site from the newly created URL.</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now, you have live versions of your projects and you're able to deploy them manually on Netlify. </p>
<p>There are additional steps to take in order to add a custom domain but we're not covering that in this tutorial. </p>
<p>This also makes it easier to reference your projects in the meantime on your résumés, portfolio site, or anywhere else.</p>
<p>Thanks for powering through this article and happy coding! 🩷</p>
<h3 id="heading-additional-resources">📝Additional Resources:</h3>
<ol>
<li>This article is a summarized version of this <a target="_blank" href="https://www.youtube.com/watch?v=7T4w0QJtL-o">video</a>. If you'd prefer to watch and follow along, then that is the video to watch.</li>
<li>There are many other platforms such as <a target="_blank" href="https://www.cloudflare.com/en-gb/">Cloudflare</a>, <a target="_blank" href="https://www.heroku.com/">Heroku</a>, or <a target="_blank" href="https://vercel.com/">Vercel</a> to deploy your projects on. This <a target="_blank" href="https://www.youtube.com/watch?v=gcwQg8-wqQ0">video</a>, for instance, shows you how to deploy your projects onto Vercel.</li>
</ol>
<h3 id="heading-connect-with-me">🔍Connect with me!</h3>
<p>Follow me on <a target="_blank" href="https://twitter.com/chelmerrox">X</a> and <a target="_blank" href="http://www.linkedin.com/in/losalini-rokocakau">LinkedIn</a> if you like my work and want to stay updated for more.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Hide API Keys in Frontend Apps using Netlify Functions ]]>
                </title>
                <description>
                    <![CDATA[ Netlify is a popular web development platform that makes it easier to build, deploy, and manage websites. You can use Netlify to host websites, and it helps you update and release new changes. It also provides additional features such as security, us... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/hide-api-keys-in-frontend-apps-using-netlify-functions/</link>
                <guid isPermaLink="false">66d45edbd1ffc3d3eb89ddd7</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ netlify-functions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Franklin Ohaegbulam ]]>
                </dc:creator>
                <pubDate>Tue, 07 Feb 2023 23:46:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/FCC-hide-API-keys-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Netlify is a popular web development platform that makes it easier to build, deploy, and manage websites.</p>
<p>You can use Netlify to host websites, and it helps you update and release new changes. It also provides additional features such as security, user authentication and authorization services, and more.</p>
<p>This guide focuses on showing you how to set up a Netlify serverless function to hide Application Programming Interface (API) keys in a client-side application.</p>
<p>For this lesson, you will create a stock photo search engine web application, deploy it on Netlify, and make an API call to the Pixabay API using Netlify serverless functions.</p>
<p>This is the same process for front-end applications built with ReactJS, NextJS, VueJS, Angular, or other JavaScript frameworks.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>To follow along with this tutorial, you should have the following:</p>
<ul>
<li><p>Netlify account (you can sign up <a target="_blank" href="http://netlify.com">here</a>)</p>
</li>
<li><p>Basic understanding of RESTful APIs, Lambda functions, and async/await concepts.</p>
</li>
</ul>
<p>The final demo app lives in the main branch on GitHub: <code>https://netlify-func-demo.netlify.app</code></p>
<h2 id="heading-what-is-a-netlify-function">What is a Netlify function?</h2>
<p>Netlify functions are serverless or lambda functions provided by Netlify. You use them to deploy server-side code or backend logic without the need for a dedicated server.</p>
<p>The purpose of this Netlify function is to manage serverless event-driven code and send HTTP requests that return a JSON response.</p>
<blockquote>
<p>"Serverless functions, branded as Netlify Functions when running on Netlify, are a way to deploy server-side code as API endpoints" - Netlify Docs</p>
</blockquote>
<p>It securely accesses environment variables behind the scenes via the Amazon Web Services (AWS) lambda function.</p>
<p>Secret credentials such as access tokens or API keys, hidden solely using environment variables, are less secure. This is because they can be easily retrieved from the Developer Tools through the API fetch request in the browser.</p>
<p>The API keys, if hijacked, can be misused by malicious actors, which might affect your app build threshold or cost you more if it's a paid API service.</p>
<p>Other serverless functions used for running code without having to manage servers include AWS Lambda functions, Azure functions, and Google cloud functions.</p>
<h2 id="heading-how-to-set-up-a-client-side-application">How to Set Up a Client-side Application</h2>
<h3 id="heading-how-to-clone-the-demo-app">How to Clone the Demo App</h3>
<p>To get started with this tutorial, you can clone the <strong>stock photo search engine app</strong> <a target="_blank" href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/main">GitHub repository</a>. See the live preview on Netlify at <a target="_blank" href="https://netlify-func-demo.netlify.app">https://netlify-func-demo.netlify.app</a>.</p>
<p>The first step is to clone the repository:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/frankiefab100/netlify-serverless-functions-demo.git
</code></pre>
<p>Next, change to the <strong>netlify-serverless-functions-demo</strong> directory.</p>
<pre><code class="lang-bash"> <span class="hljs-built_in">cd</span> netlify-serverless-functions-demo
</code></pre>
<p>Then you'll need to install dependencies.</p>
<pre><code class="lang-bash">npm install
<span class="hljs-comment">#OR </span>
yarn add
</code></pre>
<p>Now run the development server. Run the following command to start the app on the server:</p>
<pre><code class="lang-bash">netlify dev
</code></pre>
<p>The app will be ready on <code>https://localhost:8888</code>.</p>
<p>Alternatively, you can skip the above steps if you wish to follow along by building the app from scratch. In the next step, you will build a stock photo search engine JavaScript application.</p>
<h3 id="heading-how-to-build-the-demo-app-using-javascript">How to Build the Demo App using JavaScript</h3>
<p>The first step is to set up a front-end app. Open your favorite code editor, such as VS Code.</p>
<p>Then, create a <strong>dist</strong> directory and inside it create an <strong>index.html</strong> file. Populate it with the following code:</p>
<pre><code class="lang-xml"><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">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-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">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Stock Photos Search Engine<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">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">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Search For Stock Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"search-section"</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">name</span>=<span class="hljs-string">"search"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"search"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter a keyword"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"searchBtn"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"search-btn"</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">"Search"</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">header</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"photo-wrapper"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</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">class</span>=<span class="hljs-string">"photo"</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">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./script.js"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</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>In the same <code>dist</code> directory, add the following styling to style.css:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* dist/style.css */</span>
* {
  <span class="hljs-attribute">box-sizing</span>: border-box;
  <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-selector-tag">body</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#222</span>;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Roboto"</span>, sans-serif;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100vw</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
}

<span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">100vw</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.search-section</span> {
  <span class="hljs-attribute">display</span>: inline;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">310px</span>;
}

<span class="hljs-selector-class">.search</span>,
<span class="hljs-selector-class">.search-btn</span> {
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">50px</span>;
}

<span class="hljs-selector-class">.search</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#d1f3bf</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#222</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">225px</span>;
}

<span class="hljs-selector-class">.search-btn</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#04ab04</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#f6f6f6</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">80px</span>;
  <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.3s</span> ease-in-out;
}

<span class="hljs-selector-class">.search-btn</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2dc22d</span>;
}

<span class="hljs-selector-class">.photo-wrapper</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">30px</span>;
}

<span class="hljs-selector-class">.photo-wrapper</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">200px</span>;
}
</code></pre>
<h2 id="heading-sign-up-for-a-free-account-on-pixabay">Sign Up for a Free Account on Pixabay</h2>
<p>The first step to using the <a target="_blank" href="https://pixabay.com/api/docs/">Pixabay API</a> is to sign up for an account with your email.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Pixabay-API-Documentation.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Pixabay API key section</em></p>
<p>As shown in the image above, your API key can be found below the <strong>parameters</strong> section in the <a target="_blank" href="https://pixabay.com/api/docs/">Pixabay API Docs website</a>.</p>
<h3 id="heading-create-an-environment-variable">Create an environment variable</h3>
<p>Environment variables (commonly known as "env") are combinations of key/value pairs that can affect the behavior and processes of an operating system or application.</p>
<p>Using environment variables is recommended to configure third-party services and their credentials during development.</p>
<h3 id="heading-install-dotenv">Install dotenv</h3>
<p>Once you complete the account creation on Pixabay, open your terminal and install <strong>dotenv</strong> as a package. This will enable your app to read environment variables saved in the <strong>.env</strong> file.</p>
<pre><code class="lang-javascript">npm install dotenv
#OR
yarn add -D dotenv
</code></pre>
<p>In the next step, you will save the API key in a <strong>.env</strong> file.</p>
<h3 id="heading-create-the-env-file">Create the .env file</h3>
<p>In the root directory of your app, create a <strong>.env</strong> file and store the API keys copied from your Pixabay Profile.</p>
<pre><code class="lang-plaintext">PIXABAY_API_KEY=123456-7890
</code></pre>
<p>Where <code>PIXABAY_API_KEY=123456-7890</code> represents the API key value.</p>
<p><strong>Note:</strong> Replace this key/value pair with the appropriate value.</p>
<h3 id="heading-create-a-gitignore-file">Create a .gitignore file</h3>
<p>To avoid committing sensitive files and values such as <code>node_modules</code> and <code>secret keys</code> to a public repository, create a <strong>.gitignore</strong> file in the same project root directory and add the following to it:</p>
<pre><code class="lang-plaintext">node_modules
.env
.netlify
</code></pre>
<p>The <strong>.netlify</strong> folder which contains compiled serverless functions together with other files listed will be excluded when the project is pushed to GitHub or any other version control system.</p>
<h3 id="heading-create-a-get-request-function">Create a get request function</h3>
<p>Now, you should add the fetch request logic in the <strong>script.js</strong>. You will adjust the API logic later using Netlify functions.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/* dist/script.js */</span>
<span class="hljs-keyword">const</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

<span class="hljs-keyword">const</span> searchbar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search"</span>);
<span class="hljs-keyword">const</span> submitBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search-btn"</span>);
<span class="hljs-keyword">const</span> photoWrapper = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".photo-wrapper"</span>);

submitBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
  getPhoto(searchbar.value);
  searchbar.value = <span class="hljs-string">""</span>;
});

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.keyCode === <span class="hljs-number">13</span>) {
    getPhoto(searchbar.value);
    searchbar.value = <span class="hljs-string">""</span>;
  }
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPhoto</span>(<span class="hljs-params">keyword</span>) </span>{
  <span class="hljs-keyword">const</span> apiKey = PIXABAY_API_KEY;
  <span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${apiKey}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(apiURL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-attr">accept</span>: <span class="hljs-string">"application/json"</span> },
    });
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

    <span class="hljs-keyword">let</span> imageURL = data.hits;

    imageURL.forEach(<span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
      <span class="hljs-keyword">let</span> imageElement = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"img"</span>);
      imageElement.setAttribute(<span class="hljs-string">"src"</span>, <span class="hljs-string">`<span class="hljs-subst">${result.webformatURL}</span>`</span>);
      photoWrapper.appendChild(imageElement);
    });
  } <span class="hljs-keyword">catch</span> (error) {
    alert(error);
  }
}
</code></pre>
<p><strong>Note:</strong> As mentioned earlier, if the codebase of this app is published on GitHub. The API key will still be accessible from the client side on a browser, although the <code>.env</code> file that contains the secret key was excluded.</p>
<p>To illustrate this, select the <a target="_blank" href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/testing"><code>testing</code> branch</a> of this app repository. The <a target="_blank" href="https://testing--netlify-func-demo.netlify.app/">Live site preview</a> will display the following Reference Errors in your browser console:</p>
<pre><code class="lang-bash">Uncaught ReferenceError: require is not defined
Uncaught ReferenceError: require is not defined at getPhotos
Uncaught ReferenceError: process is not defined at getPhotos
</code></pre>
<p>This is because there is no way to reference the environment variables specified in the <strong>.env</strong> file, since they weren't committed to the public repository on GitHub.</p>
<p>In the next step, select and clone the <code>[testing](https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/testing)</code> branch on your local machine with the following commands:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/frankiefab100/netlify-serverless-functions-demo.git
<span class="hljs-built_in">cd</span> netlify-serveless-functions-demo
npm install
netlify dev
</code></pre>
<p>The app should launch on your browser via <code>localhost:8888</code>.</p>
<p>Now, go to the <strong>Developer tools,</strong> right-click and select <strong>Inspect</strong>. Alternatively, press the <strong>F12</strong> key. Then, navigate to <strong>Network tab</strong> and tab on the <code>getPhotos.js</code> request URL.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--124----Copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>API key displayed in the Network tab in Developer tools</em></p>
<p>You should see the API key publicly exposed in the <strong>Network tab</strong>'s <strong>Headers</strong> section and return as a response data in your browser.</p>
<p>This is a security issue since the Network tab in the Developer tools is typically responsible to display informations such as the request URL, response status, and response data.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--127----Copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>API key returned as a response data and exposed in the URL of the request</em></p>
<p>In the next section, you will find a way to secure the API key using Netlify functions.</p>
<h2 id="heading-how-to-get-started-with-netlify-functions">How to Get Started with Netlify Functions</h2>
<p>First, you'll need to go into your terminal and install <strong>Netlify CLI</strong> and <strong>Lambda</strong> as Devdependencies. You can do that by running this command:</p>
<pre><code class="lang-bash">npm install -g netlify-cli netlify-lambda
<span class="hljs-comment">#OR </span>
yarn add -D netlify-cli netlify-lambda --save-dev
</code></pre>
<h3 id="heading-add-custom-build-and-development-commands-in-packagejson">Add custom build and development commands in package.json</h3>
<p>These commands build and start the app in the server and also launch the app on your web browser. Here's an example of how you might add these script commands in the <strong>package.json</strong> file:</p>
<pre><code class="lang-bash"><span class="hljs-string">"scripts"</span>: {
   <span class="hljs-string">"build"</span>: <span class="hljs-string">"npm run-script"</span>,
   <span class="hljs-string">"dev"</span>: <span class="hljs-string">"netlify dev"</span>
 }
</code></pre>
<h3 id="heading-install-axios">Install Axios</h3>
<p>You will use the <code>axios.get</code> method, because it is a node function unlike the <code>fetch</code> method that is intended for browser runtime.</p>
<p>To install Axios, open your terminal and enter the command:</p>
<pre><code class="lang-bash">npm install axios
<span class="hljs-comment">#OR</span>
yarn add -D axios
</code></pre>
<p>In this case, you are working with a vanilla JavaScript app, so you should import Axios in the <code>getPhotos.js</code> file as:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);
</code></pre>
<p>For JavaScript libraries, like React, import it as follows:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
</code></pre>
<h3 id="heading-create-a-serverless-function">Create a serverless function</h3>
<p>In the root of the project, create a folder named <code>netlify,</code>and inside it create another folder <code>functions</code>. In this <code>functions</code> directory, create a file named <code>getPhotos.js</code>.</p>
<p>You will create a serverless function in the <code>getPhotos</code>. This will completely hide the API keys while fetching images from the <a target="_blank" href="https://pixabay.com/api">Pixabay API</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//netlify/functions/getPhotos.js </span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { keyword } = event.queryStringParameters;
    <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> axios.get(
      <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${process.env.PIXABAY_API_KEY}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>,
      {
        <span class="hljs-attr">headers</span>: { <span class="hljs-attr">Accept</span>: <span class="hljs-string">"application/json"</span>, <span class="hljs-string">"Accept-Encoding"</span>: <span class="hljs-string">"identity"</span> },
        <span class="hljs-attr">params</span>: { <span class="hljs-attr">trophies</span>: <span class="hljs-literal">true</span> },
      }
    );

    <span class="hljs-keyword">let</span> imageURL = response.data.hits;

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ imageURL }),
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ error }),
    };
  }
};
</code></pre>
<p>Here, the <code>process.env.PIXABAY_API_KEY</code> references API key environment configuration specified in the <code>.env</code> file for development mode.</p>
<p>The <code>keyword</code> parameter accepts a string accessible in the <code>queryStringParameters</code> property and returns a response data stored in the variable <code>imageURL</code>. This will get passed to the <code>script.js</code> as request response (we'll discuss this later).</p>
<p>If the GET request is successful, it returns a response of <code>statusCode</code> 200 with the corresponding response as a JSON object. For errors, we will get an alert with the error message and status code.</p>
<p>Due to changes in version, Axios might return Buffer as a response in your terminal window, that looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-data.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Buffer response from Axios in Terminal</em></p>
<p>To prevent this, you should attach the following to the GET request:</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> axios.get(
      <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${process.env.PIXABAY_API_KEY}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>,
      {
        <span class="hljs-attr">headers</span>: { <span class="hljs-attr">Accept</span>: <span class="hljs-string">"application/json"</span>, <span class="hljs-string">"Accept-Encoding"</span>: <span class="hljs-string">"identity"</span> },
        <span class="hljs-attr">params</span>: { <span class="hljs-attr">trophies</span>: <span class="hljs-literal">true</span> },
      }
    );
</code></pre>
<h3 id="heading-create-a-netlify-configuration-file">Create a Netlify configuration file</h3>
<p>In the project root directory, create a <code>netlify.toml</code> file. This file specifies how Netlify builds and deploys your app.</p>
<p>Now, add the following build configurations in <code>netlify.toml</code>:</p>
<pre><code class="lang-bash">[build]
  <span class="hljs-built_in">command</span> = <span class="hljs-string">"npm run build"</span>
  <span class="hljs-built_in">functions</span> = <span class="hljs-string">"netlify/functions"</span>
  publish = <span class="hljs-string">"dist"</span>
</code></pre>
<p><strong>Note:</strong></p>
<ul>
<li><p><code>command = "npm run build"</code> triggers the Netlify CLI to build the app from the functions.</p>
</li>
<li><p><code>functions = "netlify/functions"</code> indicates that the <code>getPhotos</code> functions exist in the <code>netlify/functions</code> directory.</p>
</li>
<li><p><code>publish = "dist"</code> identifies <code>dist</code> as the directory where the file will be served from.</p>
</li>
</ul>
<h3 id="heading-update-the-scriptjs-file-with-the-netlify-functions-request-url">Update the script.js file with the Netlify functions request URL</h3>
<p>Next, update the <code>apiURL</code> from this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${apiKey}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>;
</code></pre>
<p>to the functions HTTP request endpoint:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`/.netlify/functions/getPhotos?keyword=<span class="hljs-subst">${keyword}</span>`</span>;
</code></pre>
<p>This serverless function will get queried to the client-side of your app through the endpoint: <code>/.netlify/functions/getPhotos</code>. Once a fetch request is sent, the <code>getphotos</code> function will be invoked and accessed in the <code>script.js.</code></p>
<p>The <code>getPhotos</code> Netlify functions' response <code>imageURL</code> will be passed and the data accessed as the value of the <code>keyword</code> parameter in the query string of function. It will get loop through to return three images from the Pixabay API to the client-side.</p>
<p>The <strong>script.js</strong> file should look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/* dist/script.js */</span>
<span class="hljs-keyword">const</span> searchbar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search"</span>);
<span class="hljs-keyword">const</span> submitBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search-btn"</span>);
<span class="hljs-keyword">const</span> photoWrapper = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".photo-wrapper"</span>);

submitBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
  getPhoto(searchbar.value);
  searchbar.value = <span class="hljs-string">""</span>;
  photoWrapper.innerHTML = <span class="hljs-string">""</span>;
});

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.keyCode === <span class="hljs-number">13</span>) {
    getPhoto(searchbar.value);
    searchbar.value = <span class="hljs-string">""</span>;
    photoWrapper.innerHTML = <span class="hljs-string">""</span>;
  }
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPhoto</span>(<span class="hljs-params">keyword</span>) </span>{
  <span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`/.netlify/functions/getPhotos?keyword=<span class="hljs-subst">${keyword}</span>`</span>;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(apiURL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-attr">accept</span>: <span class="hljs-string">"application/json"</span> },
    });
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; data.imageURL.length; i++) {
      <span class="hljs-keyword">let</span> imageElement = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"img"</span>);
      imageElement.setAttribute(<span class="hljs-string">"src"</span>, <span class="hljs-string">`<span class="hljs-subst">${data.imageURL[i].webformatURL}</span>`</span>);
      photoWrapper.appendChild(imageElement);
    }
  } <span class="hljs-keyword">catch</span> (error) {
    alert(error);
  }
}
</code></pre>
<p><strong>Note:</strong> From the codebase above, your environment variable is secure since it is accessed from the serverless function.</p>
<h3 id="heading-run-the-development-server">Run the development server</h3>
<p>Now, execute your app by running the command:</p>
<pre><code class="lang-bash">netlify dev
</code></pre>
<p>This will load the <strong>getPhotos</strong> function via <code>https://localhost:8888/.netlify/functions/getPhotos.</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-dev-terminal.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>netlify build terminal output</em></p>
<p>Then, start the development server and launch the application in your default web browser on <code>localhost:8888</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-success2.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>netlify function ready on https://localhost:8888</em></p>
<p>At this point, the Netlify function is fully setup, and you can now proceed to make <code>GET</code> HTTP requests.</p>
<h3 id="heading-how-to-send-fetch-requests">How to Send Fetch Requests</h3>
<p>Now that you already have the web app served, go ahead and send a fetch request. Enter some text in the search input field and hit <strong>Enter</strong> or click the button to fetch images from the Pixabay API.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Stock-Photos-Search-Engine--1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>flower images fetched from Pixabay API</em></p>
<p>For more information about the Pixabay API, see the <a target="_blank" href="https://pixabay.com/api/docs">Pixabay documentation</a>.</p>
<p>The preceding command will send a request to the serverless function and then return six responses. Here's what the response looks like in your terminal windows:</p>
<blockquote>
<p>Request from ::1: GET /.netlify/functions/getPhotos?keyword=flower<br>Response with status 200 in 4895 ms.</p>
</blockquote>
<p>You can also use <strong>Developer tools</strong> through the <strong>Network</strong> tab to validate the request.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--118----Copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>serverless function API response data</em></p>
<p>The fetch request returns our app URL, the <strong>getPhotos</strong> Netlify function, script.js and the images from Pixabay.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--120-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Netlify function fetch response from Network tab's header section in the browser</em></p>
<h2 id="heading-how-to-host-the-app-on-the-remote-repository">How to Host the App on the Remote Repository</h2>
<p>Now, you should push your project to GitHub version control.</p>
<pre><code class="lang-javascript">git add .
git commit -m<span class="hljs-string">"initial commit"</span>
git push origin -u main
</code></pre>
<h2 id="heading-how-to-deploy-the-app-and-serverless-function-on-netlify">How to Deploy the App and Serverless Function on Netlify</h2>
<p>Once you have published the project on GitHub, log in to <a target="_blank" href="https://app.netlify.com">Netlify</a> and connect your GitHub account by granting authorization.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--130-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Import project for deployment on Netlify</em></p>
<p>As shown in the image above, click the <code>Add A New Project</code> and then search for the app repository from the list. Next, click on <code>Build Your Site</code>. This will take a few minutes to complete.</p>
<p>You just deployed the app from the Netlify UI. Your app is now ready at: <code>https://netlify-func-demo.netlify.app.</code>.</p>
<p>The fetch request URL should look like this: <code>https://netlify-func-demo.netlify.app/.netlify/functions/getPhotos</code>.</p>
<h2 id="heading-optional-how-to-configure-the-netlify-app-from-the-dashboard">Optional - How to Configure the Netlify App from the Dashboard</h2>
<p>Alternatively, you can configure the Netlify command from the Netlify Dashboard. If these settings are already specified in <strong>netlify.toml</strong>, it will override any corresponding configuration.</p>
<p>First, select the project directory's Site settings.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Site-overview-netlify-func-demo1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Site settings for project directory on Netlify</em></p>
<h3 id="heading-set-the-build-command-and-publish-directory">Set the Build command and Publish directory</h3>
<p>Navigate to <code>Site settings</code> &gt; <code>Deploy</code> &gt; <code>Build &amp; deploy</code> and edit the following commands and then save the changes:</p>
<ul>
<li><p>Build command: <strong>npm run build</strong></p>
</li>
<li><p>Publish directory: <strong>dist</strong></p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--132-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Build and deploy section on Netlify</em></p>
<h3 id="heading-set-the-functions-directory">Set the Functions directory</h3>
<p>By default, Netlify uses <code>netlify/functions</code> as the directory to find the functions to deploy them. In our case, our serverless function <code>getPhotos</code> can be found in the <code>netlify/functions</code> directory.</p>
<p>Now we'll set a custom functions directory so Netlify can find your compiled functions. Go to <code>Site settings</code> &gt; <code>Functions</code> and enter the directory path where the functions are stored in your repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--131-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Functions directory section on Netlify</em></p>
<h3 id="heading-how-to-set-environment-variables-for-production">How to Set Environment Variables for Production</h3>
<p>In the Netlify Dashboard, click on <code>Site settings</code> &gt; <code>Build &amp; deploy</code> &gt; <code>Environment</code> &gt; <code>Environment variables</code> and then configure KEY/VALUE pairs as follows:</p>
<pre><code class="lang-plaintext">PIXABAY_API_KEY=your-api-key-here
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--133-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Environment variables section on Netlify</em></p>
<p>Click on Save, and then redeploy your app so the changes can be added.</p>
<p>To redeploy the app on Netlify, navigate to <code>Deploys</code> &gt; <code>Trigger deploy</code>. Next, select <code>Clear cache and redeploy site</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--134-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Trigger redeployment tab on Netlify</em></p>
<p><strong>Note:</strong> The environment variable (PIXABAY_API_KEY) name should match the one mentioned in the <code>getPhotos</code> codebase.</p>
<p>For a React application, prepend the API environment variable with the prefix <code>REACT_APP</code> so they can be read from the <code>.env</code> file.</p>
<pre><code class="lang-plaintext"> REACT_APP_PIXABAY_API_KEY=your-api-key-here
</code></pre>
<h2 id="heading-how-to-initialize-the-serverless-function-to-the-remote-app">How to Initialize the Serverless Function to the Remote App</h2>
<p>To connect your project directory to the existing web app deployed on Netlify, login to your Netlify account from the terminal:</p>
<pre><code class="lang-bash">netlify login
</code></pre>
<p>Next, initialize the app on Netlify by running the command in your terminal:</p>
<pre><code class="lang-bash">netlify init
</code></pre>
<p>Your app is now configured for continuous deployment via Netlify.</p>
<h2 id="heading-how-to-build-the-netlify-serverless-function">How to Build the Netlify Serverless Function</h2>
<p>You need to link your app to site ID on Netlify before running the build command on your terminal. To connect your local project folder to its site ID on Netlify, enter the command in your terminal:</p>
<pre><code class="lang-bash">netlify link
</code></pre>
<p>This will prompt you to link the folder to a site through any of the following options:</p>
<ul>
<li><p>Search by full or partial site name</p>
</li>
<li><p>Choose from a list of your recently updated sites</p>
</li>
<li><p>Enter a site ID</p>
</li>
</ul>
<p>Once you select your preferred option, it will connect the project folder to the hosted site on Netlify. This allows you run <strong>Netlify CLI</strong> commands and also automatically deploy the project repository once there are any changes in the codebase.</p>
<p>In the next step, you will build a serverless function while it is running on the server. To activate the build command defined in <code>netlify.toml</code> file, run the following command:</p>
<pre><code class="lang-bash">netlify build
</code></pre>
<p>This runs the <code>npm run-script</code> command under the hood as specified in the <code>package.json</code>. Now, your serverless function in <code>netlify/functions</code> directory is packaged and bundled successfully!</p>
<h2 id="heading-how-to-test-the-application">How to Test the Application</h2>
<p>To test and confirm that the Netlify function is working fine, run this command on your terminal:</p>
<pre><code class="lang-bash">netlify <span class="hljs-built_in">functions</span>:serve
</code></pre>
<p>This injects your project environment variables from the <strong>.env</strong> file and runs the serverless function.</p>
<h3 id="heading-how-to-confirm-api-keys-security">How to Confirm API Keys Security</h3>
<p>To inspect your app and confirm that the API keys are hidden from the public, follow the steps below:</p>
<p>Click on your hosted app URL, and navigate to <strong>Developer tools</strong> by pressing the <strong>F12</strong> key or right-clicking and selecting <strong>Inspect</strong>. Navigate to <strong>Network</strong> tab, where you should see the fetched data from the Pixabay API.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--128-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>API key hidden from the public and unauthorized parties after inspection through the Developer tools</em></p>
<p>Now you've confirmed that you've successfully configured a serverless function and have it deployed on Netlify.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This tutorial introduced serverless functions, asynchronous JavaScript, and Restful API concepts.</p>
<p>Hopefully you now know how to create a serverless/lambda function and secure any sensitive value such as API keys in your frontend JavaScript application.</p>
<p>If you got stuck with anything, you can access the complete source code in this <a target="_blank" href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/main">GitHub repository</a>.</p>
<p>Thank you for reading! If you have any questions, feel free to reach out to me via <a target="_blank" href="https://twitter.com/frankiefab100">Twitter</a>.</p>
<h2 id="heading-relevant-links"><strong>Relevant Links</strong></h2>
<ul>
<li><p><a target="_blank" href="https://docs.netlify.com/functions/overview/">Netlify Functions</a></p>
</li>
<li><p><a target="_blank" href="https://www.netlify.com/blog/intro-to-serverless-functions/">Netlify Intro to Serverless Functions</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/sdras/netlify-functions-example">Netlify Functions Example</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a React Router App to Netlify and Fix the "Page Not Found" Error ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever experienced a “PAGE NOT FOUND” error when trying to access or refresh an application that uses React Router? Don't worry, you're not alone. In this article, you will learn how to deploy a React application that uses react-router without... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-a-routed-react-app-to-netlify/</link>
                <guid isPermaLink="false">66c8c860c4cede4e0083f72e</guid>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react router ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Israel Chidera ]]>
                </dc:creator>
                <pubDate>Mon, 03 Oct 2022 17:16:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/netlify1-1-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever experienced a “PAGE NOT FOUND” error when trying to access or refresh an application that uses React Router? Don't worry, you're not alone.</p>
<p>In this article, you will learn how to deploy a React application that uses react-router without any problems.</p>
<h2 id="heading-the-problem-with-deploying-react-router-apps">The Problem with Deploying react-router Apps</h2>
<p>To make sure your users are happy, you'll typically prioritize user experience when building an application. Good user experience ensures that your website or application is easy to understand, easy to use, and easy to navigate. </p>
<p>If you have published a React app to Netlify that uses React router before now, you will likely notice that while navigating through your routes, you get an error (<strong>page not found</strong>) when you refresh your browser. This creates a bad user experience. </p>
<p>Through this article, you will learn how to deploy a React app through the Netlify CLI and how to resolve the problem with deploying a react-router app.</p>
<h2 id="heading-what-is-netlify">What is Netlify?</h2>
<p>According to <a target="_blank" href="https://docs.netlify.com/">their docs</a>, "Netlify is an all-in-one platform for automating modern web projects." </p>
<p>Netlify helps web developers build and deploy their applications instantly. This handy tool replaces your hosting infrastructure and helps facilitate your continuous integration and deployment pipeline with one workflow. It can really help increase your productivity.</p>
<p>The Netlify CLI (Command Line Interface) helps you easily build, test, and deploy applications straight from your command line. </p>
<p>It lets you:</p>
<ol>
<li>start a project in seconds</li>
<li>configure continuous deployment</li>
<li>run a local development server that you can share with other developers</li>
<li>deploy your projects globally</li>
</ol>
<p>You can sign up for a <a target="_blank" href="http://netlify.app">Netlify account</a> with your email or your Git account.<br>If you don't have a Git account, you can create a GitHub, GitLab, or bitbucket account in no time.</p>
<p>So, let’s see how you can deploy your react-router app using the Netlify CLI.</p>
<h2 id="heading-how-to-deploy-your-app-through-the-netlify-cli">How to Deploy Your App Through the Netlify CLI</h2>
<p>To start using Netlify’s CLI, you must have Node.js installed on your computer. You can visit <a target="_blank" href="https://nodejs.org/en/download/">here</a> to install Node.js. </p>
<p>Then you can get started by installing the Netlify CLI using this command:</p>
<pre><code class="lang-js">npm install netlify-cli -g
</code></pre>
<p>Now that you have installed Netlify’s CLI, you can deploy your application to Netlify. Before that, you might want to get your build folder (I'll explain why below). </p>
<p>To get your build folder, type the following command:</p>
<pre><code class="lang-js">yarn run build
<span class="hljs-comment">//or</span>
npm run build
</code></pre>
<p>If you have not logged in to your Netlify account before now, you might see a pop-up window asking for permission to access Netlify.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/netlify3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After getting access to the Netlify CLI, you will get a prompt asking what you would like to do. Select the option to <strong>create and configure a new site</strong>. You can use your arrow keys to navigate between options.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/netlify4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will get prompts to select a team and a site name for your app. You can either input your preferred name for your app or use the default name which you can change later.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/netlify5.1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will get a prompt asking which directory to publish. Input <strong>build</strong> as your deploy folder and press enter to get your React app deployed. The build folder which was generated at the beginning 0f this tutorial will serve as the deploy path.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/netlify7.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you get a final response that states <strong>“If everything looks good on your draft URL, deploy it to your main site URL with the --prod flag”</strong>, you are on the right track. You will be provided with the website draft URL to preview your app.</p>
<p>You can deploy to the main site by running the following command:</p>
<pre><code class="lang-js">netlify deploy --prod
</code></pre>
<p>Awesome. You now have your website URL.</p>
<h2 id="heading-how-to-fix-the-page-not-found-error">How to Fix the "Page Not Found" Error</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/netlify1.png" alt="Image" width="600" height="400" loading="lazy">
<em>page not found, netlify error</em></p>
<p>To avoid the page-not-found error whenever you are either trying to access your app or you're on other routes, you have to set up a redirect and rewrite rules for your react-router application. </p>
<p>The redirect rule will help your application avoid a 404 error. All requests will get redirected to the /index.html instead of giving a 404 error.</p>
<p>To configure your redirect rules, you have to create a file that does not have an extension named (_redirects) inside your public folder.</p>
<p>Include the following command in the _redirects file:</p>
<pre><code class="lang-_redirects">/*    /index.html  200
</code></pre>
<p>To view the changes in your app, you'll have to deploy it again with the following command:</p>
<pre><code class="lang-js">netlify deploy
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article has explained how to deploy a react-router app using Netlify’s CLI and fix the page-not-found error while accessing your routes.</p>
<p>I hope you have found this article useful.</p>
<p><strong>Keep building, and keep deploying!</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy Multiple Repositories to Subfolders Under One Website with Netlify ]]>
                </title>
                <description>
                    <![CDATA[ By Abdulmalik Salawu Hi there! 👋 You're probably here because you are trying to deal with hosting two separate websites or repositories under one website using Netlify. And maybe you've checked out the answers on the Netlify community page, but you ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-multiple-repos-to-subfolders-under-one-website-netlify/</link>
                <guid isPermaLink="false">66d460c533b83c4378a51830</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 13 Jul 2022 14:57:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/pexels-xxss-is-back-777001.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Abdulmalik Salawu</p>
<p>Hi there! 👋 You're probably here because you are trying to deal with hosting two separate websites or repositories under one website using Netlify.</p>
<p>And maybe you've checked out the answers on the Netlify community page, but you are still confused.</p>
<p>The same confusion and headache led me to write this tutorial so you don't have to struggle as much as I did.</p>
<p>It's tricky but it works: the solution to this problem is the <code>netlify.toml</code> or <code>_redirects file</code>.</p>
<p>Let's jump in.</p>
<p>I am working on a project with some colleagues, and we split up the tasks so that I'm working on the documentation.</p>
<h2 id="heading-technologies-well-use-here">Technologies We'll Use Here</h2>

<ul>
<li><a target="_blank" href="https://docusaurus.io/docs/">Docusaurus</a> for shipping beautiful documentation sites in no time.</li>
<li>Next.js/React.js for our main website</li>
</ul>
<p>Let's get started and see how we can host the documentation. We dan do this in two ways:</p>
<ol>
<li>Using a subdomain, docs.mainsite.dev</li>
<li>Using the main domain mainsite.dev and hosting the docs as a subdirectory on mainsite.dev/docs.</li>
</ol>
<p>Based on what I have seen being implemented by other projects in their documentation, I wanted to host it as a subfolder via Netlify too. </p>
<p>I figure it'd help our docs have that professional look.</p>
<p>If you will be hosting docusaurus as the subfolder too, you need to do some set up.</p>
<h2 id="heading-step-1-update-the-base-url">Step 1 – Update the Base URL</h2>
<p>Change the <strong>baseUrl</strong> in your docusaurus.config.js file to "<strong>/docs/"</strong> with the following code:</p>
<pre><code class="lang-javascript">  title: <span class="hljs-string">'Your Docs Title'</span>,
  <span class="hljs-attr">tagline</span>: <span class="hljs-string">'Your Docs Tagline'</span>,
  <span class="hljs-attr">url</span>: <span class="hljs-string">'my-docs-site.netlify.app'</span>,
  <span class="hljs-attr">baseUrl</span>: <span class="hljs-string">"/docs/"</span>,
  <span class="hljs-attr">onBrokenLinks</span>: <span class="hljs-string">'throw'</span>,
  <span class="hljs-attr">onBrokenMarkdownLinks</span>: <span class="hljs-string">'warn'</span>,
  <span class="hljs-attr">favicon</span>: <span class="hljs-string">'/favicon.ico'</span>,
</code></pre>
<p>By changing your base URL to <strong>/docs/</strong>, it makes your website render exactly as <strong>https://mainsite.dev/docs/</strong>, which is the docs path.</p>
<p>If you were to set the <strong>baseUrl</strong> to "/" there will be error. This also means we don't have to deal with proxying our docs site.</p>
<h2 id="heading-step-2-update-the-routebasepath">Step 2 – Update the routeBasePath</h2>
<p>You'll also need to make sure that the documentation contents are served from your root domain by changing the <strong>routeBasePath</strong> to <strong>'/'</strong>. </p>
<p>Just like you see in the snippet below:</p>
<pre><code class="lang-javascript">  presets: [
    [
      <span class="hljs-string">'classic'</span>,
      <span class="hljs-comment">/** @type {import('@docusaurus/preset-classic').Options} */</span>
      ({
        <span class="hljs-attr">docs</span>: {
          <span class="hljs-attr">routeBasePath</span>: <span class="hljs-string">'/'</span>,
        },
      }),
    ],
  ],
</code></pre>
<p>This will help you activate the docs only mode on docusaurus. This way, your docs will be served from your root domain but with the path '/docs/' being the base path.</p>
<p>After that, your can run <code>npx docusaurus start</code> on your localhost to see if your docs site will build and render well without issues. </p>
<p>If you have no issues, then you should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/docusaurus-run-localhost-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Your docusaurus site should render like this on path '/docs/' being the base path.</em></p>
<p>To read more about the config for docusaurus docs only mode, you can read <a target="_blank" href="https://docusaurus.io/docs/docs-introduction#docs-only-mode">this</a>.</p>
<h2 id="heading-step-3-deploy-to-netlify">Step 3 – Deploy to Netlify</h2>
<p>Now it's time to deploy to your docs site to Netlify. If you don't already know how to do that, you can refer to this <a target="_blank" href="https://docusaurus.io/docs/deployment#deploying-to-netlify">guide</a>.</p>
<p>Once you are done deploying, your Netlify website URL should be available already like this: // my-docs-site.netlify.app.</p>
<h2 id="heading-step-4-proxying">Step 4 – Proxying</h2>
<p>Here comes the part where you will be doing the proxying.</p>
<p>You have already hosted your main website on Netlify and your docs site has been deployed to Netlify too.</p>
<p>Now you need to create a <em>netlify.toml</em> file in the root of your project/repository of your main website, and add the following lines to it:</p>
<pre><code class="lang-markdown">[[redirects]]
from = "/docs/<span class="hljs-emphasis">*"
to = "https://my-docs-site.netlify.app/:splat"
status = 200
force = false</span>
</code></pre>
<p>The above rule makes sure that, whenever the /docs/ path is queried on your main site, your docs site loads up normally on your main-website.netlify.app/docs/ path.</p>
<p>Alternatively you can do this proxying via your docs site/repository. Just create a netlify.toml file in the root of your docs site/repository, and add the following lines to it:</p>
<pre><code class="lang-toml"><span class="hljs-section">[[redirects]]</span>
<span class="hljs-attr">from</span> = <span class="hljs-string">"/*"</span>
<span class="hljs-attr">to</span> = <span class="hljs-string">"https://main-website.netlify.app/docs/:splat"</span>
<span class="hljs-attr">status</span> = <span class="hljs-number">301</span>
<span class="hljs-attr">force</span> = <span class="hljs-literal">true</span>
</code></pre>
<p>The above rule makes sure that whenever the /* path is queried on your docs site, it will load up normally on your main-website.netlify.app/docs/ path.</p>
<p>You will also notice that your docs site on the Netlify site is broken – but it works perfectly on your main site. </p>
<p>Since it works and we have achieved our goals, then let it be 😁.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/docs-site-netlify-error.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Broken docusaurus site for subfolder documentation</em></p>
<p><strong>NOTE:</strong> Never add the rules to your docs site and your main site at the same time, as this will cause a conflict of "TOO MANY REDIRECTS" errors.</p>
<p>So you either add the rules to your docs site or your main site, but not both.</p>
<h2 id="heading-lets-answer-some-questions">Let's Answer Some Questions</h2>
<p><strong>Q:</strong> Why would I choose to use the <em>netlify.toml</em> file, and not the _<em>redirects</em> file?</p>
<p>Yeah, I first went for the easy option and tried the <em>redirects file too. But it wasn't that easy because you will have to always copy the __redirects</em> file into your <strong>build/</strong> or <strong>public/</strong> folder while building your Netlify site.</p>
<p>This requires you to edit your Netlify build settings to something like this:</p>
<pre><code class="lang-txt">npm run build &amp;&amp; cp _redirects public/
</code></pre>
<p>You can also achieve the proxying using the _<em>redirects</em> file. The rules will be in the following format for main-site:</p>
<pre><code class="lang-txt">/docs/* https://my-docs-site.netlify.app/:splat 200
</code></pre>
<p>and in this format for your docs site:</p>
<pre><code class="lang-txt">/* https://main-website.netlify.app/docs/:splat 301!
</code></pre>
<p><strong>Q:</strong> Why would I choose to use Netlify URLs in all the proxying rules instead of our custom domain URL?</p>
<p>Well, the Netlify community advises that you use the Netlify URLs, since it is a more reliable way to do the proxying.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations 🎉, glad you made it to the end of this guide. </p>
<p>I believe you've learnt something new today.</p>
<p>It's time to go implement and make your project documentation look professional too by having it hosted in the subfolder of your main site. 👏.</p>
<p>You can also share this article, so that others can see it.</p>
<h2 id="heading-resources">Resources:</h2>
<ul>
<li><a target="_blank" href="https://play.netlify.com/redirects">Netlify Rules Playground</a></li>
<li><a target="_blank" href="https://answers.netlify.com/t/support-guide-can-i-deploy-multiple-repositories-in-a-single-site/179">[Support Guide] Can I deploy multiple repositories in a single site?</a>   </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Serverless Online Payments with Netlify and Stripe ]]>
                </title>
                <description>
                    <![CDATA[ By Alain Perkaz One of the first steps many young startups take is setting up a static web page, perhaps with an email newsletter, to help them build an audience.  As the weeks go by and the MVP is getting further along, the subject of how to handle ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/serverless-online-payments/</link>
                <guid isPermaLink="false">66d45d9ccc7f04d2549a3728</guid>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ payments ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 17 Nov 2021 20:54:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/11/----1--2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alain Perkaz</p>
<p>One of the first steps many young startups take is setting up a static web page, perhaps with an email newsletter, to help them build an audience. </p>
<p>As the weeks go by and the MVP is getting further along, the subject of how to handle payments will eventually emerge.</p>
<p>From one-time payments to SaaS subscriptions, supporting online payments can be daunting and time-consuming. This post will introduce you to an easy way to process online payments with Stripe, without any extra infrastructure other than a static web page.</p>
<p>You don't need a custom backend to store the payment information, or cron jobs to send invoices, and there's no need to track customers in a separate database. This is perfect if you are a single-founder or early-stage startup that wants to validate the idea without creating a custom solution.</p>
<p>Sounds good? Let's dive in! 🤿</p>
<h2 id="heading-high-level-overview-of-the-project">High-level overview of the project</h2>
<p>For the sake of this article, we'll define an early-stage startup use case, where this kind of serverless online payment setup will bring the most value (low cost and fast to implement).</p>
<p>Let's imagine our startup idea is to self-publish a book. As the book is being finalized, we would like to open the lifetime access to the book as a pre-sale.</p>
<p>We will need a way to process payments for lifetime access to the book. For this, we'll need a payment processor and perhaps a way to run some logic away from the client (for example, leveraging the payment processor's API).</p>
<h3 id="heading-payment-processor">Payment processor</h3>
<p>There are plenty of payment processors available, each with different terms, support for payment methods, and public APIs. For our serverless online payment processor, we'll use <a target="_blank" href="https://stripe.com/">Stripe</a>. I chose to use Stripe for two reasons:</p>
<p>First, Stripe is an industry-leading payment processor with an excellent API. Their API is extensively documented, and they offer integration SDKs for many languages (JS included). Setting it up is entirely free, and you only pay a small commission per processed transaction.</p>
<p>Second, Stripe offers Stripe Checkout, a free product specifically built to boost conversions and support various payment options. It's dead-simple to integrate and comes with a great UI.</p>
<h3 id="heading-what-about-the-server">What about the server?</h3>
<p>To be clear, Stripe requires some server-side code to generate a session once a user inputs their payment data. The session is available to the developer to perform payment-related operations (without exposing the sensitive payment details).</p>
<p>Before you get really upset with me, let me clarify that we won't need to set up a dedicated server 😅. It may seem a bit contradictory, but Stripe requires that some of the interaction code is in a server-<strong>like</strong> environment (serverless computing to the rescue!).</p>
<p>Luckily for us, this is 2021, and there are quite a few options to execute on-demand server-side code. Most cloud providers offer this functionality (AWS lambdas, Google Cloud cloud functions, Azure functions…you name it).</p>
<p>Since our startup already has a web page, we'll use <a target="_blank" href="https://www.netlify.com/products/functions/">Netlify functions</a>. It will allow us to run the server-side code with almost no extra configuration, and it plays nicely with the existing web page statics. </p>
<p>The paradigm of combining static web assets with on-demand serverless functions is part of the <a target="_blank" href="https://jamstack.org/">JAM Stack</a> (we'll leave that for another post). Keep reading for the detailed instruction on how to set up serverless payments.</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635610448861_image.png" alt="Image" width="600" height="400" loading="lazy">
<em>High-level schema of the solution</em></p>
<h1 id="heading-step-by-step-project-setup">Step-by-step project setup</h1>
<p>Great, now that you have a clear picture of the problem space and the tools we'll use to build our solution, let's build it. 🛠</p>
<p>The complete code example is available at <a target="_blank" href="https://github.com/aperkaz/serverless-payments">https://github.com/aperkaz/serverless-payments</a>.</p>
<h3 id="heading-how-to-set-up-netlify">How to set up Netlify</h3>
<p>First, create a <a target="_blank" href="https://www.netlify.com/">Netlify</a> account (if you don't have one already). The free tier is enough for moderate usage, so no need to worry about that. </p>
<p>Netlify provides CI/CD for automated deployments of our webpage and serverless functions by connecting to a Git repo in Github / Gitlab / Bitbucket. So, let's create a repo in one of those providers with your website assets.</p>
<p>Next, install the <a target="_blank" href="https://cli.netlify.com/getting-started/">Netlify CLI</a>. It will ask you some questions and request access to your Netlify and Git repo provider (GitHub in my case).</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635614098858_Screenshot+2021-10-30+at+19.14.47.png" alt="Image" width="600" height="400" loading="lazy">
<em>Installing the CLI with <code>npm install netlify-cli -g</code></em></p>
<p>At this point, we can push to the repository’s <code>main</code> / <code>master</code> branch, and Netlify will automatically deploy. You can run <code>netlify open</code> in the console to open Netlify’s admin panel, and visit the deployed URL.</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635614710558_Screenshot+2021-10-30+at+19.19.40.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Excellent, with the auto-deploy ready, now let's set up Stripe. 💸</p>
<h3 id="heading-how-to-set-up-stripe">How to set up Stripe</h3>
<p>Create an account in Stripe, validate the email, and sign in. Then, <a target="_blank" href="https://stripe.com/docs/keys">generate a set of API keys</a> (Secret key and Publishable key).</p>
<p>You have to be careful with those keys and never commit the Secret key in the code. Since we will need it in our server-side code, we'll keep it as an <a target="_blank" href="https://www.netlify.com/blog/2021/07/12/managing-environment-variables-from-your-terminal-with-netlify-cli/">environment variable</a>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new env variable in Netlify</span>
netlify env:<span class="hljs-built_in">set</span> STRIPE_SECRET <span class="hljs-string">"sk_****"</span>

<span class="hljs-comment"># We can access it on our server-side JS code by:</span>
process.env.STRIPE_SECRET
</code></pre>
<p>For the sake of this tutorial, we will use the <a target="_blank" href="https://stripe.com/docs/payments/accept-a-payment?integration=checkout#set-up-stripe">example API keys</a>, but feel free to use your own. If you use your keys, you will need to add products and prices (<a target="_blank" href="https://support.stripe.com/questions/how-to-create-products-and-prices">documentation</a>).</p>
<h3 id="heading-how-to-add-the-serverless-functions">How to add the serverless functions</h3>
<p>Hang on tight – we are almost there! We only need the server-side code to create Stripe Checkout sessions and complete our demo.</p>
<p>First, to make our function accessible from <a target="_blank" href="https://serverless-payments.netlify.app/api/stripe">https://serverless-payments.netlify.app/api/stripe</a>, we need to add some configurations. Let's start by creating the <code>netlify.toml</code> file, on the root of our repo.</p>
<pre><code class="lang-toml"><span class="hljs-section">[build]</span>
  <span class="hljs-attr">command</span> = <span class="hljs-string">"# no build command"</span>
  <span class="hljs-attr">functions</span> = <span class="hljs-string">"netlify/functions"</span>
  <span class="hljs-attr">publish</span> = <span class="hljs-string">"."</span>

<span class="hljs-section">[[redirects]]</span>
  <span class="hljs-attr">from</span> = <span class="hljs-string">'/api/*'</span>
  <span class="hljs-attr">to</span> = <span class="hljs-string">'/.netlify/functions/:splat'</span>
  <span class="hljs-attr">status</span> = <span class="hljs-number">200</span>
</code></pre>
<p>Then, we can add the session creator function. It’s explained <a target="_blank" href="https://stripe.com/docs/payments/accept-a-payment?integration=checkout#set-up-stripe">here</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// netlify/function/stripe.js</span>

<span class="hljs-keyword">const</span> stripe = <span class="hljs-built_in">require</span>(<span class="hljs-string">"stripe"</span>)(process.env.STRIPE_SECRET);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> stripe.checkout.sessions.create({
    <span class="hljs-attr">payment_method_types</span>: [<span class="hljs-string">"card"</span>],
    <span class="hljs-attr">line_items</span>: [
      {
        <span class="hljs-attr">price_data</span>: {
          <span class="hljs-attr">currency</span>: <span class="hljs-string">"usd"</span>,
          <span class="hljs-attr">product_data</span>: {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"T-shirt"</span>,
          },
          <span class="hljs-attr">unit_amount</span>: <span class="hljs-number">2000</span>,
        },
        <span class="hljs-attr">quantity</span>: <span class="hljs-number">1</span>,
      },
    ],
    <span class="hljs-attr">mode</span>: <span class="hljs-string">"payment"</span>,
    <span class="hljs-attr">success_url</span>: <span class="hljs-string">"https://serverless-payments.netlify.app/success"</span>,
    <span class="hljs-attr">cancel_url</span>: <span class="hljs-string">"https://serverless-payments.netlify.app/cancel"</span>,
  });
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
      <span class="hljs-attr">id</span>: session.id,
    }),
  };
};
</code></pre>
<p>Now we can call the serverless functions from our JS body with <code>fetch("/api/stripe")</code>. It will scale depending on the load and you only paid for the invocations. Then it will be deployed on every push to <code>main</code>. Sweet! 🍬</p>
<p>For the sake of brevity, I skipped the remaining code in the HTML files that handles the Stripe Checkout callbacks. The code is available <a target="_blank" href="https://github.com/aperkaz/serverless-payments">here</a>.</p>
<p>The complete example is available at <a target="_blank" href="https://serverless-payments.netlify.app/">https://serverless-payments.netlify.app</a> . You can test a successful payment flow by using <code>4242 4242 4242 4242</code> as a credit card number.</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635620255253_Screenshot+2021-10-30+at+20.57.20.png" alt="Image" width="600" height="400" loading="lazy">
<em>Stripe Checkout in all its glory, accessible from [our page](https://serverless-payments.netlify.app" rel="noreferrer nofollow noopener)</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Online payments are critical to many online businesses but are often implemented in a rush or are over-engineered. The solution presented above applies to single-page applications, so you may not need a fully-fledged server for handling payments just yet. 🙂</p>
<p>I hope this article helps shed some light on adding payment processing to your existing web pages easily. Sell your product quickly and make customers happy!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Publish an HTML Website on Netlify or GitHub Pages ]]>
                </title>
                <description>
                    <![CDATA[ By Vasyl Lagutin You have finished creating your HTML website and you're feeling proud of your hard work. But there is one thing that is still missing: you have no idea how to publish your website. In this tutorial, you will learn how to publish an H... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/publish-your-website-netlify-github/</link>
                <guid isPermaLink="false">66d45e06182810487e0ce133</guid>
                
                    <category>
                        <![CDATA[ github pages ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 28 Sep 2021 17:36:52 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/How-to-publish-an-HTML-website--3-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Vasyl Lagutin</p>
<p>You have finished creating your HTML website and you're feeling proud of your hard work. But there is one thing that is still missing: you have no idea how to publish your website.</p>
<p>In this tutorial, you will learn how to publish an HTML website using two popular platforms – <strong>Netlify</strong> and <strong>GitHub</strong>.</p>
<p>Before we start, make sure that you have a <a target="_blank" href="https://github.com">GitHub account</a> because you will need to host your repository (your source code) on GitHub. Without it, you won't be able to publish your HTML website following this tutorial.</p>
<h2 id="heading-how-to-publish-a-website-on-netlify">How to Publish a Website on Netlify</h2>
<p>The first method we're going to explore is how to publish your website on <a target="_blank" href="https://www.netlify.com/">Netlify</a>.</p>
<p>Netlify is a platform for hosting websites. It is easy to host sites on Netlify as you don't need to configure it manually – and best of all, it's free. If you haven't signed up for an account, now is a good time to do so.</p>
<p>Here's the step-by-step process of publishing your website on Netlify:</p>
<h3 id="heading-step-1-add-your-new-site">Step 1: Add your new site</h3>
<p>Once you've logged in, it will take you to a home dashboard. Click the <strong>New site from git</strong> button to add your new website to Netlify.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-2-link-to-your-github">Step 2: Link to your GitHub</h3>
<p>When you click the <strong>New site from git</strong> button, it will take you to the "Create a new site" page. Make sure that you push your repository on GitHub so that Netlify can link to your GitHub account.</p>
<p>Click the <strong>GitHub</strong> button as shown in the screenshot below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-3-authorize-netlify">Step 3: Authorize Netlify</h3>
<p>Next, click the <strong>Authorize Netlify by Netlify</strong> button. This permission is needed so that both Netlify and GitHub can connect.</p>
<h3 id="heading-step-4-select-your-repository">Step 4: Select your repository</h3>
<p>Once you grant permission to Netlify, you can see a list of all your repositories. Select your website to publish. You can find it by either scrolling down the list or using the search bar to narrow down the list.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-5-configure-your-settings">Step 5: Configure your settings</h3>
<p>After selecting your website, you will be prompted to configure the settings for deploying the website. Since your website is simply a static one, there's not much to do here. Just click <strong>Deploy site</strong> to continue.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_5-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-6-publish-your-website">Step 6: Publish your website</h3>
<p>Your website is now ready to publish! Netlify will do the rest of the work for you, and it will only take a minute or so to complete the process.</p>
<p>Now you are done! Your new website is published, and you can view it by clicking the green link.</p>
<p>Right now, your URL looks random, but you can edit it by clicking the <strong>Site settings</strong> button and then the <strong>Change site name</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_6-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_7.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Congratulation on publishing your first new website! Now we'll learn how to publish a website with GitHub.</p>
<h2 id="heading-how-to-publish-a-website-on-github">How to Publish a Website on GitHub</h2>
<p>The second method we'll look at uses GitHub to publish your site. GitHub is a platform for storing, tracking, and managing project source code. It is also where you can publish your HTML website – and like Netlify, it is free to host here.</p>
<p>Here's the step-by-step process of publishing your website on GitHub:</p>
<blockquote>
<p>Note: You can only publish your website on GitHub if you set the repository's visibility to public. If you want to deploy a website while it is private, upgrade your account to Pro or use Netlify to host there instead.</p>
</blockquote>
<h3 id="heading-step-1-go-to-your-websites-repository">Step 1: Go to your website's repository</h3>
<p>After you've logged in, go to the repository on the left sidebar and select the one you want to publish.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-2-select-the-settings">Step 2: Select the settings</h3>
<p>In your repository, click the <strong>Settings</strong> link, and it will take you to the repository's settings page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-3-go-to-github-pages">Step 3: Go to GitHub Pages</h3>
<p>When you're in a repository's settings, scroll down a bit until you see the <strong>Pages</strong> link on the left sidebar. Click it, and it will lead you to GitHub Pages.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-4-select-the-branch">Step 4: Select the branch</h3>
<p>In the source section, click the dropdown and select the master branch and save it. Depending on how you name it, it can be <strong>master</strong> or <strong>main</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-5-all-done">Step 5: All done</h3>
<p>And you are done! Your website will be published, and it will take only a minute or so to complete the process. Refresh the page, and you will see a link to your newly published website.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_5.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you've found this tutorial helpful. You have learned how to publish your HTML website with Netlify and GitHub. Now you can go ahead and show the world of your incredible work.</p>
<p>If you're looking to learn more about modern web-development, I invite you to join my <a target="_blank" href="https://js.coderslang.com">Full-Stack JavaScript Course</a> or <a target="_blank" href="https://learn.coderslang.com">read more articles about JS, HTML and CSS at my programming blog.</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Securely Access Secret API keys using Netlify Functions in a React App ]]>
                </title>
                <description>
                    <![CDATA[ In this article, you will learn how to securely access secret API keys using Netlify functions in a React app. Netlify provides rich features that help you easily deploy Single Page Applications built using frameworks like React, Vue and Angular amon... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-access-secret-api-keys-using-netlify-functions-in-a-react-app/</link>
                <guid isPermaLink="false">66d45f63246e57ac83a2c773</guid>
                
                    <category>
                        <![CDATA[ Application Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ create-react-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ information security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joseph Mawa ]]>
                </dc:creator>
                <pubDate>Mon, 28 Jun 2021 21:17:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/pexels-noelle-otto-906018.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, you will learn how to securely access secret API keys using Netlify functions in a React app.</p>
<p>Netlify provides rich features that help you easily deploy Single Page Applications built using frameworks like <a target="_blank" href="https://reactjs.org/">React</a>, <a target="_blank" href="https://v3.vuejs.org/">Vue</a> and <a target="_blank" href="https://angular.io/">Angular</a> among others. This takes away the burden of coding and maintaining server-side code.</p>
<p>In some cases, a front-end app needs to communicate with an external third-party API. Some of the third-party APIs require secret API keys to access.</p>
<p>Let's imagine a situation where you want to include weather alerts on your front-end app. As a result, you sign up for <a target="_blank" href="https://openweathermap.org/api">open weather map</a> API's paid plan which requires a secret API key to access.</p>
<p>In such situations, you'll need to take care to ensure that you don't expose the secret API key on the front end.</p>
<p>Netlify provides functionality on its web user interface which you can use to hide API keys. But the API key can be accessed from the client side if the environment variable in which it is stored is accessed from the front-end code.</p>
<h2 id="heading-what-youll-learn-in-this-article">What You'll Learn in This Article</h2>
<p>In this article, you are going to hide the secret API key on the Netlify UI and securely access it using Netlify functions in a React app created using <a target="_blank" href="https://create-react-app.dev/">create-react-app(CRA)</a>. Using Netlify functions makes sure that the API key is not exposed on the client-side.</p>
<p>The process should be similar for other frameworks, though we are using <a target="_blank" href="https://reactjs.org/">React</a> in this article.</p>
<p>By the end of this article, you will be able to do the following:</p>
<ul>
<li><p>Add a Netlify function to a React app</p>
</li>
<li><p>Use Netlify functions to securely access secret API keys</p>
</li>
<li><p>Use the <a target="_blank" href="https://docs.netlify.com/cli/get-started/">netlify-cli</a> tool to test your Netlify functions</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Outlined below are some of the prerequisites for this article. It is worth pointing out that you can still follow along even if you don't check off every one.</p>
<p>You can Google if there is something you don't understand or post a question on the <a target="_blank" href="https://forum.freecodecamp.org/">freeCodeCamp forum</a>. We shall be happy to help.</p>
<ul>
<li><p>Have at least a basic understanding of <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">JavaScript</a>, the <a target="_blank" href="https://reactjs.org/">React</a> framework, and a version control system like <a target="_blank" href="https://git-scm.com/">Git</a>.</p>
</li>
<li><p>Have <a target="_blank" href="https://nodejs.org/en/">Node</a> installed on your machine. If you don't have it installed, you can download it for your system from the <a target="_blank" href="https://nodejs.org/en/download/">Node downloads page</a>.</p>
</li>
<li><p>Have <a target="_blank" href="https://git-scm.com/">Git</a> installed on your machine. If you don't have it installed, you can do so from the <a target="_blank" href="https://git-scm.com/downloads">Git downloads page</a>.</p>
</li>
<li><p>Have a text editor like <a target="_blank" href="https://code.visualstudio.com/">VS code</a> or <a target="_blank" href="https://atom.io/">Atom</a> installed.</p>
</li>
<li><p>Have a <a target="_blank" href="https://www.netlify.com/">Netlify</a> account. If you don't have one, you can <a target="_blank" href="https://app.netlify.com/signup">sign up</a> at no cost using your email address.</p>
</li>
<li><p>Have a basic knowledge of Netlify's <a target="_blank" href="https://docs.netlify.com/site-deploys/create-deploys/#deploy-with-git">continuous deployment</a> feature. You will use it to deploy your React app to Netlify from <a target="_blank" href="https://github.com/">GitHub</a>.</p>
</li>
<li><p>Have a <a target="_blank" href="https://github.com/">GitHub</a> account because we'll be using Netlify's <a target="_blank" href="https://docs.netlify.com/site-deploys/create-deploys/#deploy-with-git">continuous deployment</a> feature. If you don't have an account, you can <a target="_blank" href="https://github.com/">sign up</a> using your email address.</p>
</li>
</ul>
<h2 id="heading-how-to-use-environment-variables-in-a-react-app-created-using-create-react-app">How to Use Environment Variables in a React App Created using <code>create-react-app</code></h2>
<p>In this section, you will learn how to use environment variables in a React app created using <a target="_blank" href="https://create-react-app.dev/"><code>CRA</code></a>. If you are already familiar with how to do this, then you can skip to the next section.</p>
<p>React apps created using <a target="_blank" href="https://create-react-app.dev/">CRA</a> come configured so you can create custom environment variables in <code>.env</code> file and then access them in your codebase using <code>process.env</code>.</p>
<p>To use this feature, you can follow the steps below. These steps assume you have already created a React app using <a target="_blank" href="https://create-react-app.dev/">CRA</a>.</p>
<h3 id="heading-step-1-create-a-env-file-at-the-root-of-the-project-directory">Step 1 - Create a <code>.env</code> file at the root of the project directory</h3>
<p>Start by creating a <code>.env</code> file at the root of the project directory. You can then add your environment variables in the <code>.env</code> file so that they look like the below.</p>
<pre><code class="lang-shell">REACT_APP_FIRST_SECRET=12345678
REACT_APP_SECOND_SECRET=123456789
</code></pre>
<p>In the above <code>.env</code> file, the environment variables are <code>REACT_APP_FIRST_SECRET</code> and <code>REACT_APP_SECOND_SECRET</code>. Their corresponding values are on the right-hand side. You need to take note of a few things when using environment variables with <a target="_blank" href="https://create-react-app.dev/">CRA</a>:</p>
<ul>
<li><p>The environment variable should always start with <code>REACT_APP</code> and then be followed by the variable name for it to work. For example, you can name the variable containing your API key <code>REACT_APP_API_KEY</code>.</p>
</li>
<li><p>There should be no spacing before and after the <code>=</code>.</p>
</li>
</ul>
<h3 id="heading-step-2-access-the-environment-variable-in-your-app-using-processenv">Step 2 - Access the environment variable in your app using <code>process.env</code></h3>
<p>You can then access those environment variables in your React app using <code>process.env.REACT_APP_FIRST_SECRET</code> and <code>proces.env.REACT_APP_SECOND_SECRET</code>. These variables are added to your codebase at build time, so you should restart your development server if you are running the app on <code>localhost</code> for the changes to take effect.</p>
<p>Accessing the environment variable like that prevents you from pushing your secret API key to a remote Git hosting service like <a target="_blank" href="https://github.com/">GitHub</a>.</p>
<p><a target="_blank" href="https://create-react-app.dev/">CRA</a> adds the <code>.gitignore</code> file by default. You just need to add <code>.env</code> file to it so that Git will ignore your <code>.env</code> file when you commit your changes.</p>
<p>What you have just learned about environment variables will keep your secrets safe in development.</p>
<p>But what will happen if you do the same thing in production since the environment variables are added to your codebase at build time? The next section will answer that question.</p>
<h2 id="heading-accessing-environment-variables-from-a-react-app-created-using-creat-react-app-exposes-your-api-keys">Accessing environment variables from a React app created using <code>creat-react-app</code> exposes your API keys</h2>
<p>Yes, indeed it does. Unfortunately, some absolute beginners think otherwise. That included me when I was just starting. But even the <a target="_blank" href="https://create-react-app.dev/">create-react-app documentation</a> states that:</p>
<blockquote>
<p>Environment variables are embedded into the build, meaning anyone can view them by inspecting your app's files - <a target="_blank" href="https://create-react-app.dev/">create-react-app documentation</a></p>
</blockquote>
<p>To illustrate this, I have built a <a target="_blank" href="https://netlify-secrets-demo.netlify.app/">simple demo app</a> and deployed it to Netlify. This is a simple React app created using <a target="_blank" href="https://create-react-app.dev/">CRA</a>. If you are interested, you can fork the <a target="_blank" href="https://github.com/nibble0101/netlify-secrets-demo-app">project repository</a> and deploy the app to Netlify under your account.</p>
<p>In this app, I am fetching a placeholder todo item from the <a target="_blank" href="https://jsonplaceholder.typicode.com/todos">JSON placeholder API</a> and then displaying it to the user.</p>
<p>The <a target="_blank" href="https://jsonplaceholder.typicode.com/todos">JSON placeholder API</a> does not need an API key to access. But for this illustration, I am using the base URL as the "secret" which I don't want to expose.</p>
<p>Most web-based APIs require passing the API key as a query parameter when authorizing users. While deploying the app, I have set the value of <code>REACT_APP_TODO_BASE_URL</code> to <code>https://jsonplaceholder.typicode.com/todos</code> on Netlify's web interface.</p>
<p>There are two ways you can access the value which I have hidden in the environment variable from the front-end:</p>
<ol>
<li><p>By inspecting the app's codebase</p>
</li>
<li><p>By inspecting the Network tab in the <a target="_blank" href="https://developer.chrome.com/docs/devtools/">Chrome DevTools</a></p>
</li>
</ol>
<h3 id="heading-how-to-inspect-the-apps-codebase">How to inspect the app's codebase</h3>
<p>To inspect the app's codebase, follow the steps below:</p>
<ol>
<li><p>Navigate to the deployed <a target="_blank" href="https://netlify-secrets-demo-app.netlify.app/">demo app</a>.</p>
</li>
<li><p>Open the browser developer tools. You can open them by pressing the key combination <code>CTRL + SHIFT + I</code> on Chrome or right-clicking and then selecting the Inspect option in Chrome. Click the <code>Sources</code> tab. This is what it looks like for me in Chrome if the <code>Sources</code> tab is active. It might look different in other browsers though.</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2021/06/005-01-sources-tab.png" alt="005-01-sources-tab" width="600" height="400" loading="lazy"></p>
</li>
<li><p>In the <code>Sources</code> tab, you should see <code>top</code> folder under the <code>Page</code> tab. Then navigate from the <code>top</code> folder to <code>main.e54a1b49.chunk.js</code> by following the path <code>top/netlify-secrets-demo-app.netlify.app/static/js/main.e54a1b49.chunk.js</code>. <code>main.e54a1b49.chunk.js</code> is a one-line minifed file that is not readable.</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2021/06/005-02-sources-folder.png" alt="005-02-sources-folder" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Click the<code>{}</code> symbol at the bottom left of the panel to pretty-print the code in a readable format.</p>
</li>
<li><p>The secret that we hid in the environment variable is right there in the codebase on line 37 as shown in the image below.</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2021/06/005-03-pretty-code.png" alt="005-03-pretty-code" width="600" height="400" loading="lazy"></p>
</li>
</ol>
<h3 id="heading-how-to-inspect-the-newtwork-tab-in-the-devtoolshttpsdeveloperchromecomdocsdevtools">How to inspect the Newtwork tab in the <a target="_blank" href="https://developer.chrome.com/docs/devtools/">Devtools</a></h3>
<ol>
<li><p>Navigate to the deployed <a target="_blank" href="https://netlify-secrets-demo-app.netlify.app/">demo app</a></p>
</li>
<li><p>Open the browser developer tool and then open the Network Tab. The network Tab should look like in the image below in chrome. It might look different in other browsers.</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2021/06/005-04-network-tab.png" alt="005-04-network-tab" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Click the <code>Get another todo</code> button in the browser. You should be able to see another row in the open panel indicating that another request has been made. Mine looks like in the image below.</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2021/06/005-05-network-request-made.png" alt="005-05-network-request-made" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Click the last row. Another panel will open showing the request and response headers. Again our "secret" has been exposed.</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2021/06/005-06-response-header-open.png" alt="005-06-response-header-open" width="600" height="400" loading="lazy"></p>
</li>
</ol>
<p>As you can see from the above, your API key may not be visible in the codebase that has been committed to <a target="_blank" href="https://github.com/">GitHub</a> but it is still accessible on the client-side. Now you know what would happen if your key is for a paid plan.</p>
<p>To keep your secret key secret, you need to access your environment variable using <a target="_blank" href="https://www.netlify.com/products/functions/">Netlify functions</a>.</p>
<p>In the next section, you will learn how to securely access environment variables using Netlify functions. You will do that by adding Netlify functions to a React app and then deploying it to Netlify.</p>
<h2 id="heading-how-to-securely-access-environment-variables-with-netlify-functions">How to Securely Access Environment Variables with Netlify Functions</h2>
<p>In this section, you will fork a simple React app which was created using <a target="_blank" href="https://create-react-app.dev/">CRA</a> and then add a Netlify function to it. You will then use your function to access the environment variable instead of accessing it from your front-end code.</p>
<p>This will ensure that you don't expose your secret API key as illustrated in the section above. You will then deploy the app to Netlify using Netlify's <a target="_blank" href="https://docs.netlify.com/site-deploys/create-deploys/#deploy-with-git">continuous deployment</a> feature.</p>
<h3 id="heading-what-is-a-netlify-function">What is a Netlify function?</h3>
<p>This is a function that you can use for executing server-side code without having to deploy your own server.</p>
<p>According to the <a target="_blank" href="https://docs.netlify.com/functions/overview/">documentation</a>, if you use Netlify functions, you are indirectly using AWS’s serverless Lambda functions which are used for running on-demand, server-side code without having to run a dedicated server.</p>
<p>Below are some of the reasons why you might need to use Netlify functions.</p>
<ul>
<li><p>Fetch live data from an API</p>
</li>
<li><p>Return dynamic images</p>
</li>
<li><p>Send automated emails</p>
</li>
</ul>
<p>To start using Netlify functions, create a folder at the root of the project directory and name it <code>netlify</code>.</p>
<p>Inside the Netlify folder, you have to create another folder called <code>functions</code>. In the <code>functions</code> folder, you can create a file that contains a function that executes your code. As a result, the path to the files containing your functions should be <code>netlify/functions</code>.</p>
<p>This is the default location where Netlify will look for your functions. If you want to change the directory where your functions are located inside <code>netlify</code> folder, then you need to add that information to a <code>netlify.toml</code> configuration file at the root of the project directory so that Netlify knows where to look for them.</p>
<p>Using <code>netlify.toml</code> is outside the scope of this article. We shall be using the default configuration.</p>
<p>Below is what a Netlify function looks like. Let's assume that I have created a <code>todo.js</code> file in <code>netlify/functions</code> and added the code below to it.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event, context</span>) </span>{
  <span class="hljs-comment">//Securely access environment variables here</span>
};
</code></pre>
<p>You will notice that the function has two parameters, <code>event</code> and <code>context</code>. If the function needs a dependency, make sure you add it to your project's <code>package.json</code> file.</p>
<p>In this case, I have added <code>axios</code> as a dependency. This function is executed whenever we hit the <code>netlify/functions/todo</code> endpoint.</p>
<p>There are other ways of triggering Netlify functions, but for this article let's focus on the simplest use case.</p>
<p>Data that have been passed from the front-end can be accessed in the <code>event</code> parameter. In the body of the function, you can do whatever you want including securely accessing your API key and sending data back to the front-end.</p>
<p>That is what you need to know to start using Netlify functions. If you want to dive deeper and explore what else you can do with them, take a look at the <a target="_blank" href="https://docs.netlify.com/functions/overview/">documentation</a>.</p>
<p>Now that you understand the basics of <a target="_blank" href="https://docs.netlify.com/functions/overview/">Netlify functions</a>, follow the steps below to learn how to implement them in a codebase.</p>
<p>You will fork <a target="_blank" href="https://github.com/nibble0101/netlify-secrets-demo-app">this demo app</a> and then add Netlify functions to it. It is a simple React project created using <a target="_blank" href="https://create-react-app.dev/">CRA</a>.</p>
<h3 id="heading-step-1-how-to-fork-the-project-under-your-own-github-account">Step 1 - How to fork the project under your own GitHub account</h3>
<p>In this step, you are going to fork the <a target="_blank" href="https://github.com/nibble0101/netlify-secrets-demo-app">demo app</a> under your own GitHub account. It is necessary to fork the project under your account so that you will be able to deploy it to Netlify.</p>
<p>If you are not interested in forking the project but would like to implement Netlify functions to your project, then skip to step 6.</p>
<p>If you don't know how to fork a GitHub repository, you can follow the steps described in the <a target="_blank" href="https://docs.github.com/en/get-started/quickstart/fork-a-repo">how to fork a repo</a> section of the GitHub documentation.</p>
<h3 id="heading-step-2-how-to-clone-the-project-to-your-local-machine">Step 2 - How to clone the project to your local machine</h3>
<p>In this step, you are going to clone the project to your local machine by running the command below (assuming you forked the project under your accoun). Do not forget to replace <code>GITHUB_USER_NAME</code> with your GitHub username.</p>
<pre><code class="lang-shell">git clone git@github.com:GITHUB_USER_NAME/netlify-secrets-demo-app.git
</code></pre>
<p>or</p>
<pre><code class="lang-shell">git clone https://github.com/GITHUB_USER_NANE/netlify-secrets-demo-app.git
</code></pre>
<p>After successfully cloning the project to your machine, you should be able to see the <code>netlify-secrets-demo-app</code> folder containing your project in the directory where the project was cloned.</p>
<p>You can navigate to the project directory and open it in your favorite text editor.</p>
<p>In the next step, you will install dependencies.</p>
<h3 id="heading-step-3-how-to-install-dependencies">Step 3 - How to install dependencies</h3>
<p>In this step, you will install dependencies by running the command below on the terminal.</p>
<pre><code class="lang-shell">npm install
</code></pre>
<p>The above command will install the dependencies you need. The installation process might take a couple of minutes so you need to be patient.</p>
<p>In the next step, you will create a <code>.env</code> file and add environment variables to it.</p>
<h3 id="heading-step-4-how-to-create-a-env-file">Step 4 - How to create a <code>.env</code> file</h3>
<p>In this step, you are going to create a <code>.env</code> file at the root of the project directory by running the command below on the terminal:</p>
<pre><code class="lang-shell">touch .env
</code></pre>
<p>You should be able to see the <code>.env</code> file created at the root of the project directory. Copy and paste the contents of the <code>example.env</code> file in it.</p>
<p>Since we'll be using Netlify functions to access environment variables, you don't need to prefix the variable name with <code>REACT_APP</code> as described at the beginning of the article. Change <code>REACT_APP_TODO_BASE_URL</code> environment variable to <code>TODO_BASE_URL</code> and set its value to <code>https://jsonplaceholder.typicode.com/todos</code>.</p>
<p>In the next step, you will add Netlify functions to your app.</p>
<h3 id="heading-step-5-how-to-add-netlify-functions-to-your-app">Step 5 - How to add Netlify functions to your app</h3>
<p>In this step, you will add a Netlify function to the app and use it to securely access your environment variables.</p>
<p>As I mentioned above, by default, Netlify will look for your functions inside the <code>functions</code> directory which must be located inside the <code>netlify</code> folder.</p>
<p>If you are keeping the functions in a different directory inside the <code>netlify</code> folder, then you need to provide additional information inside <code>netlify.toml</code> configuration file at the root of the project directory. This will make sure that Netlify knows where to locate your functions. But in this article, we'll be using the Netlify default configuration.</p>
<p>Create a folder at the root of the project directory and name it <code>netlify</code>. In the <code>netlify</code> folder create another folder and call it <code>functions</code>. In the <code>functions</code> folder, create <code>todo.js</code> file.</p>
<p>Copy and paste the code below in the <code>todo.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event, context</span>) </span>{
  <span class="hljs-built_in">console</span>.log(event);
  <span class="hljs-built_in">console</span>.log(context);
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { id } = event.queryStringParameters;
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">`<span class="hljs-subst">${process.env.TODO_BASE_URL}</span>/<span class="hljs-subst">${id}</span>`</span>);
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">title</span>: response.data.title }),
    };
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">statusCode</span>: <span class="hljs-number">404</span>,
      <span class="hljs-attr">body</span>: err.toString(),
    };
  }
};
</code></pre>
<p>We'll send data from the front-end as the value of the <code>id</code> parameter in the query string. It is accessible in the <code>queryStringParameters</code> property of the <code>event</code> object.</p>
<p>You can also <code>console.log</code> the <code>event</code> and <code>context</code> parameters to see what their properties are.</p>
<p>We securely access the environment variable and use <code>axios</code> to fetch our todo. If it is successful, we send back a response object with a <code>statusCode</code> of 200 with the data in the body of the response object. If there is an error we return a <code>statusCode</code> of 404 and the error is sent back in the body of the response object.</p>
<p>The function you have added above will be exposed to your front-end code via <code>/.netlify/functions/todo</code> endpoint. It will be executed whenever you hit the <code>/.netlify/functions/todo</code> endpoint. Now let's execute the function from the front-end.</p>
<p>Navigate to the <code>App.js</code> component in the <code>src</code> folder. In the <code>useEffect</code> hook on line 15, instead of accessing <code>process.env.</code> on the front-end, we instead make a <code>GET</code> request to our endpoint exposed by the Netlify function we declared in the previous step.</p>
<p>So change line 15 from:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> url = <span class="hljs-string">`<span class="hljs-subst">${process.env.REACT_APP_TODO_BASE_URL}</span>/<span class="hljs-subst">${todoId}</span>`</span>;
</code></pre>
<p>to:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> url = <span class="hljs-string">`/.netlify/functions/todo?id=<span class="hljs-subst">${todoId}</span>`</span>;
</code></pre>
<p>Your <code>App.js</code> component should now look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [todoId, setTodoId] = useState(<span class="hljs-number">1</span>);
  <span class="hljs-keyword">const</span> [todo, setTodo] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getNewTodo</span>(<span class="hljs-params"></span>) </span>{
    setTodoId(<span class="hljs-function">(<span class="hljs-params">todoId</span>) =&gt;</span> (todoId === <span class="hljs-number">20</span> ? <span class="hljs-number">1</span> : todoId + <span class="hljs-number">1</span>));
  }

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchTodo</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">const</span> url = <span class="hljs-string">`/.netlify/functions/todo?id=<span class="hljs-subst">${todoId}</span>`</span>;
      <span class="hljs-keyword">try</span> {
        setLoading(<span class="hljs-literal">true</span>);
        <span class="hljs-keyword">const</span> todo = <span class="hljs-keyword">await</span> fetch(url).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> res.json());
        setTodo(todo.title);
      } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-built_in">console</span>.log(err);
      } <span class="hljs-keyword">finally</span> {
        setLoading(<span class="hljs-literal">false</span>);
      }
    }
    fetchTodo();
  }, [todoId]);

  <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">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{getNewTodo}</span>&gt;</span> Get another todo <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{loading ? "Loading..." : todo}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>In the code above we are storing <code>todo</code> and <code>todoId</code> in state. Notice that the <code>GET</code> request we are making to the <code>/.netlify/functions/todo</code> endpoint in the <code>useEffect</code> hook. We are passing the <code>todoId</code> as the value of the query parameter <code>id</code>.</p>
<p>After making the fetch request, our <code>todo.js</code> function will be invoked. Inside <code>todo.js</code> as explained above, we'll access the environment variable and fetch the todo which is returned by the function for the front-end code to use. This keeps our environment variable safe since it is not accessible from the front-end. The front-end only consumes what the function returns.</p>
<p>In the next step, you will test whether the function you have just defined works as intended.</p>
<h3 id="heading-step-6-how-to-test-your-netlify-function">Step 6 - How to test your Netlify function</h3>
<p>In this step, you will use <a target="_blank" href="https://docs.netlify.com/cli/get-started/">netlify-cli</a> to test whether the function you defined works as intended.</p>
<p>Run the command below to install <code>netlify-cli</code> globally. The installation will take a bit of time, so be patient:</p>
<pre><code class="lang-js">npm install netlify-cli -g
</code></pre>
<p>After <code>netlify-cli</code> is installed globally, run this command to test your function:</p>
<pre><code class="lang-js">netlify dev
</code></pre>
<p>Running the above command successfully will start a local development server on port 8888. You will also see the <code>event</code> and <code>context</code> parameters printed on the console when your function is invoked.</p>
<p>You can also test the function by running the command below on another terminal. Make sure the server is running before running the command below otherwise you will get an error.</p>
<pre><code class="lang-js">netlify functions:invoke --querystring <span class="hljs-string">"id=1"</span>
</code></pre>
<p>This command will invoke the function with the specified query string. You will be prompted to make some selections on the terminal. Just press enter.</p>
<p>Successfully running the above command will fetch our todo which will then be printed on the terminal.</p>
<pre><code class="lang-js">{<span class="hljs-string">"title"</span>:<span class="hljs-string">"delectus aut autem"</span>}
</code></pre>
<p>In the next step, you will deploy the project on your local machine to GitHub.</p>
<h3 id="heading-step-7-how-to-commit-your-changes-and-push-to-github">Step 7 - How to commit your changes and push to GitHub</h3>
<p>In this step, you will commit the changes on your local machine and push them to GitHub using the commands below.</p>
<pre><code class="lang-shell">git commit -m "Add netlify functions"
git push origin master
</code></pre>
<p>In the next step, you will use netlify's <a target="_blank" href="https://docs.netlify.com/site-deploys/create-deploys/#deploy-with-git">continuous ceployment</a> feature to deploy the app from GitHub.</p>
<h3 id="heading-step-8-how-to-deploy-the-app-to-netlify-from-github">Step 8 - How to deploy the app to Netlify from GitHub</h3>
<p>In this step, you will use Netlify's <a target="_blank" href="https://docs.netlify.com/site-deploys/create-deploys/#deploy-with-git">continuous deployment</a> feature to deploy from GitHub.</p>
<p>This step requires you to have a Netlify account. If you haven't signed up, you can do so from the <a target="_blank" href="https://app.netlify.com/signup">signup page</a> at no cost.</p>
<p>Log in to your Netlify account and follow the process for linking a GitHub repository to Netlify for continuous deployment <a target="_blank" href="https://docs.netlify.com/configure-builds/get-started/#basic-build-settings">as described in the documentation</a>. Do not forget to add the environment variable <code>TODO_BASE_URL</code> and set its value to <code>https://jsonplaceholder.typicode.com/todos</code> under the advanced settings as you deploy the app to Netlify.</p>
<p>And there you have it! That is how you hide secret API keys using Netlify functions. I hope you enjoyed reading the article.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article we learned how to:</p>
<ul>
<li><p>Add a Netlify function to a React app</p>
</li>
<li><p>Use Netlify functions to securely access secret API keys</p>
</li>
<li><p>Use the <a target="_blank" href="https://docs.netlify.com/cli/get-started/">netlify-cli</a> tool to test your Netlify functions</p>
</li>
</ul>
<p>With <a target="_blank" href="https://www.netlify.com/products/functions/">netlify's serverless functions</a>, you can send emails, fetch data from an API like we just did, and much more. It has equipped front-end developers with the tools to write back-end code without worrying about server maintenance.</p>
<p>You can explore more about what else you can do with Netlify functions in the <a target="_blank" href="https://docs.netlify.com/functions/overview/">functions section of the documentation</a>.</p>
<p>Finally, if you have any questions about what we've covered in this article, feel free to ask on the <a target="_blank" href="https://forum.freecodecamp.org/">freeCodeCamp forum</a> or DM me on <a target="_blank" href="https://twitter.com/MJMAWA">Twitter</a>. You can also ask your question in the <a target="_blank" href="https://answers.netlify.com/">Netlify forum</a>.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Portfolio Website with React ]]>
                </title>
                <description>
                    <![CDATA[ Today you're going to create one of the most important apps you can build for yourself: your developer portfolio. Every React developer or web developer in general needs to be able to show off what they can do to any potential client or employer. Tha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-portfolio-website-react/</link>
                <guid isPermaLink="false">66d0375bc1024fe75b758f37</guid>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Reed ]]>
                </dc:creator>
                <pubDate>Mon, 21 Jun 2021 18:15:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/react-portfolio-2021.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Today you're going to create one of the most important apps you can build for yourself: your developer portfolio.</p>
<p>Every React developer or web developer in general needs to be able to show off what they can do to any potential client or employer.</p>
<p>That's exactly what we're going to be building right now, with the help of a number of industry standard tools, including React, Tailwind CSS, and Netlify.</p>
<p>Let's get started!</p>
<h2 id="heading-what-will-the-portfolio-look-like">What Will the Portfolio Look Like?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/portfolio-1-min.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is the final version of the portfolio you will be building.</p>
<p>It will feature information about yourself, what projects you have made, what skills you've used in making those projects, and will include a contact form for clients or employers to reach out to you.</p>
<h2 id="heading-what-tools-will-we-be-using">What Tools Will We Be Using?</h2>
<ul>
<li>We will use React to create the app's user interface. It will allow us to compose each part of our landing page through reusable components and extend our app if we want to add additional features, such as a blog.</li>
<li>To style our application, we will use Tailwind CSS. To give our app a professional appearance, Tailwind will allow us to easily apply multiple styles through combining classnames on our React elements. </li>
<li>For pushing our app to the web, we will use the free service Netlify. It will serve our project on a custom domain to users very quickly with the help of a CDN (content delivery network).</li>
</ul>
<h2 id="heading-how-to-get-started">How to Get Started</h2>
<p><strong><a target="_blank" href="https://reedbarger.com/resources/react-portfolio">You can download the starting files for our project here</a>.</strong></p>
<p>When you grab the code, all you will have to do is drag your (unzipped) project folder into your code editor and run the command:</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>And you're good to go!</p>
<h2 id="heading-what-tools-do-i-need-to-build-my-portfolio">What Tools Do I Need to Build my Portfolio?</h2>
<p>To go through the entire process of creating our app from start to deployment, you will need to have the following:</p>
<ol>
<li>Node.js installed on your computer. You can download it at nodejs.org.</li>
<li>Git installed on your computer. You can download it at git-scm.com.</li>
<li>I would recommend using VS Code as your code editor. You can download it at code.visualstudio.com.</li>
<li>A free Netlify account at netlify.com.</li>
<li>A free GitHub account at github.com.</li>
</ol>
<h2 id="heading-how-to-build-the-portfolio-structure">How to Build the Portfolio Structure</h2>
<p>The benefit of using React is that we could expand our app to as many pages as we like, very simply, and add a lot of additional content. </p>
<p>However, since we're just working with one page, we can within our app component figure out the different components that we need very quickly. We will have a Navbar on top with all of the links to jump to different sections of our portfolio. </p>
<p>After that, we will include an about section, a section for our projects, testimonials, and finally our contact form.</p>
<p>This quick planning allows us to figure out what our components should be named and in what order. We can go ahead and add all of them to our App.js file (in src):</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/App.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Projects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Skills</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Testimonials</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-create-our-components">How to Create our Components</h2>
<p>Now that we have all these components listed out we need to go ahead and create them. </p>
<p>Within our source (src) folder, we're going to create a folder called components with all of the files that we need:</p>
<pre><code>my-portfolio
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.js
    ├── data.js
    ├── index.css
    ├── index.js
    └── components
        ├── About.js
        ├── Contact.js
        ├── Navbar.js
        ├── Projects.js
        ├── Skills.js
        └── Testimonials.js
</code></pre><p>Then we will create the basic structure of each React component and export it from that file with <code>export default</code>:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/About.js</span>

<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">About</span>(<span class="hljs-params"></span>) </span>{}

<span class="hljs-comment">// repeat the same basic structure for all 6 components</span>
</code></pre>
<p>And finally make sure to import it back in App.js:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/App.js</span>

<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> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/About"</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Contact"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar"</span>;
<span class="hljs-keyword">import</span> Projects <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Projects"</span>;
<span class="hljs-keyword">import</span> Skills <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Skills"</span>;
<span class="hljs-keyword">import</span> Testimonials <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Testimonials"</span>;

<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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Projects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Skills</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Testimonials</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p><em>Note that there should be six components in total.</em> </p>
<h2 id="heading-intro-to-tailwind-css">Intro to Tailwind CSS</h2>
<p>Once that's done, we can start working with Tailwind CSS, in order to start to give our app a basic appearance. </p>
<p>The benefit of using Tailwind CSS is that we don't have to write any styles manually in a CSS stylesheet. All we have to do is combine multiple classes to create the appearance that we want. </p>
<p>For example, to give our portfolio a dark background with gray text applied to all of our child components, you can add the following classes to our <code>main</code> element:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/App.js</span>

<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> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/About"</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Contact"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar"</span>;
<span class="hljs-keyword">import</span> Projects <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Projects"</span>;
<span class="hljs-keyword">import</span> Skills <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Skills"</span>;
<span class="hljs-keyword">import</span> Testimonials <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Testimonials"</span>;

<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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-400 bg-gray-900 body-font"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Projects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Skills</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Testimonials</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-build-the-about-component">How to Build the About Component</h2>
<p>Let's start on our first section, the about section. This will consist of a basic introduction to ourselves and what skills we specialize in. </p>
<p>It's also going to include some links to the contact form as well as our past projects. Since these links will be to different parts of the same page, we can use the hashes: "/#projects" and "/#contact".</p>
<p>To make these links work and to be able to jump to each section, we will set the <code>id</code> attribute of the projects section to "projects" and those of the contact section to "contact".</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/About.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<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">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"about"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mx-auto flex px-10 py-20 md:flex-row flex-col items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"</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-font sm:text-4xl text-3xl mb-4 font-medium text-white"</span>&gt;</span>
            Hi, I'm Reed.
            <span class="hljs-tag">&lt;<span class="hljs-name">br</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"hidden lg:inline-block"</span> /&gt;</span>I love to build amazing
            apps.
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-8 leading-relaxed"</span>&gt;</span>
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui
            laborum quasi, incidunt dolore iste nostrum cupiditate voluptas?
            Laborum, voluptas natus?
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex text-white bg-green-500 border-0 py-2 px-6 focus:outline-none hover:bg-green-600 rounded text-lg"</span>&gt;</span>
              Work With Me
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">"#projects"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg"</span>&gt;</span>
              See My Past Work
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:max-w-lg lg:w-full md:w-1/2 w-5/6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"object-cover object-center rounded"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"hero"</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"./coding.svg"</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">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>For the image on the righthand side of the section, I am using an svg file from the <code>public</code> folder, coding.svg.</p>
<p>This image serves merely as a temporary placeholder. I would highly recommend using an actual image of yourself.</p>
<h2 id="heading-how-to-build-the-projects-component">How to Build the Projects Component</h2>
<p>Our projects section will consist of a <code>section</code> element with an <code>id</code> of "projects". This will feature a gallery of all the projects that we've built, which will include images.</p>
<p>It'll have the title of the project, along with the technologies we use to make it, and a link to it (if it is deployed). </p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Projects.js</span>

<span class="hljs-keyword">import</span> { CodeIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<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> { projects } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</span>;

<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">Projects</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-400 bg-gray-900 body-font"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto text-center lg:px-40"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col w-full mb-20"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">CodeIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mx-auto inline-block w-10 mb-4"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:text-4xl text-3xl font-medium title-font mb-4 text-white"</span>&gt;</span>
            Apps I've Built
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-2/3 mx-auto leading-relaxed text-base"</span>&gt;</span>
            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Explicabo
            facilis repellat ab cupiditate alias vero aliquid obcaecati quisquam
            fuga dolore.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap -m-4"</span>&gt;</span>
          {projects.map((project) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">{project.link}</span>
              <span class="hljs-attr">key</span>=<span class="hljs-string">{project.image}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:w-1/2 w-100 p-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex relative"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                  <span class="hljs-attr">alt</span>=<span class="hljs-string">"gallery"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0 w-full h-full object-cover object-center"</span>
                  <span class="hljs-attr">src</span>=<span class="hljs-string">{project.image}</span>
                /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-8 py-10 relative z-10 w-full border-4 border-gray-800 bg-gray-900 opacity-0 hover:opacity-100"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tracking-widest text-sm title-font font-medium text-green-400 mb-1"</span>&gt;</span>
                    {project.subtitle}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</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-font text-lg font-medium text-white mb-3"</span>&gt;</span>
                    {project.title}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed"</span>&gt;</span>{project.description}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</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">div</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">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>Note that we are also going to use the library <code>@heroicons/react</code> in order to be able to write some SVG icons as React components. </p>
<p>We are importing an array of projects from a data.js file in the same folder. There we are exporting an array of objects which each include an individual project's data:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/data.js</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projects = [
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"React Reserve"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"MERN Stack"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-1.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://reactbootcamp.com"</span>,
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"React Tracks"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"React and Python"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-2.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://reedbarger.com"</span>,
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"DevChat"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"React and Firebase"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-3.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://jsbootcamp.com"</span>,
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Epic Todo App"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"React Hooks"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-4.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://pythonbootcamp.com"</span>,
  },
];
</code></pre>
<h2 id="heading-how-to-build-the-skills-component">How to Build the Skills Component</h2>
<p>Let's fill out the section for all the skills and technologies that we know. </p>
<p>This will consist of a simple list of all of the major tools that we're familiar with and can use in our employers or clients projects. </p>
<p>Once again, we are going to import an array from our data folder. But this array consists of number of strings which represent each of the skills that we know such as JavaScript, React, and Node:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Skills.js</span>

<span class="hljs-keyword">import</span> { BadgeCheckIcon, ChipIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<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> { skills } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</span>;

<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">Skills</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center mb-20"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChipIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 inline-block mb-4"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:text-4xl text-3xl font-medium title-font text-white mb-4"</span>&gt;</span>
            Skills <span class="hljs-symbol">&amp;amp;</span> Technologies
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base leading-relaxed xl:w-2/4 lg:w-3/4 mx-auto"</span>&gt;</span>
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nisi sit
            ipsa delectus eum quo voluptas aspernatur accusantium distinctio
            possimus est.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap lg:w-4/5 sm:mx-auto sm:mb-2 -mx-2"</span>&gt;</span>
          {skills.map((skill) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{skill}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 sm:w-1/2 w-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-800 rounded flex p-4 h-full items-center"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">BadgeCheckIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-green-400 w-6 h-6 flex-shrink-0 mr-4"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-medium text-white"</span>&gt;</span>
                  {skill}
                <span class="hljs-tag">&lt;/<span class="hljs-name">span</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">div</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">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-build-the-testimonials-component">How to Build the Testimonials Component</h2>
<p>In the Testimonials component, we are going to list a couple of testimonials maybe from past clients or people who are familiar with our work. </p>
<p>These are going to consist of a couple of cards that feature the testimonial itself as well as who it's from and the company that this person is from. </p>
<p>We are also importing a testimonials array with a number of objects that feature the quote, image, name, and company. </p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Testimonials</span>

<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> { TerminalIcon, UsersIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<span class="hljs-keyword">import</span> { testimonials } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</span>;

<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">Testimonials</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"testimonials"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UsersIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 inline-block mb-4"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:text-4xl text-3xl font-medium title-font text-white mb-12"</span>&gt;</span>
          Client Testimonials
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap m-4"</span>&gt;</span>
          {testimonials.map((testimonial) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 md:w-1/2 w-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-full bg-gray-800 bg-opacity-40 p-8 rounded"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">TerminalIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block w-8 text-gray-500 mb-4"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed mb-6"</span>&gt;</span>{testimonial.quote}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex items-center"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">"testimonial"</span>
                    <span class="hljs-attr">src</span>=<span class="hljs-string">{testimonial.image}</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12 rounded-full flex-shrink-0 object-cover object-center"</span>
                  /&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-grow flex flex-col pl-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-medium text-white"</span>&gt;</span>
                      {testimonial.name}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm uppercase"</span>&gt;</span>
                      {testimonial.company}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</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">div</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">div</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">section</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-build-the-contact-component">How to Build the Contact Component</h2>
<p>At the end of our landing page, we're going to include our contact form to allow potential employers to reach out to us. </p>
<p>This form will have 3 inputs: a name, email, and message input. </p>
<p>To receive these form submissions, we will use the tool Netlify Forms to very easily take care of saving those messages.</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Contact.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<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">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto flex sm:flex-nowrap flex-wrap"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-2/3 md:w-1/2 bg-gray-900 rounded-lg overflow-hidden sm:mr-10 p-10 flex items-end justify-start relative"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">iframe</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">height</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">"map"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0"</span>
            <span class="hljs-attr">frameBorder</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginHeight</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginWidth</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">filter:</span> "<span class="hljs-attr">opacity</span>(<span class="hljs-attr">0.7</span>)" }}
            <span class="hljs-attr">src</span>=<span class="hljs-string">"https://www.google.com/maps/embed/v1/place?q=97+warren+st+new+york+city&amp;key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-900 relative flex flex-wrap py-6 rounded shadow-md"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                ADDRESS
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                97 Warren St. <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
                New York, NY 10007
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6 mt-4 lg:mt-0"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                EMAIL
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-indigo-400 leading-relaxed"</span>&gt;</span>
                reedbarger@email.com
              <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs mt-4"</span>&gt;</span>
                PHONE
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed"</span>&gt;</span>123-456-7890<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</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">form</span>
          <span class="hljs-attr">netlify</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/3 md:w-1/2 flex flex-col md:ml-auto w-full md:py-8 mt-8 md:mt-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white sm:text-4xl text-3xl mb-1 font-medium title-font"</span>&gt;</span>
            Hire Me
          <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed mb-5"</span>&gt;</span>
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illum
            suscipit officia aspernatur veritatis. Asperiores, aliquid?
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Name
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">"name"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Email
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
              <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Message
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 h-32 text-base outline-none text-gray-100 py-1 px-3 resize-none leading-6 transition-colors duration-200 ease-in-out"</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">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"</span>&gt;</span>
            Submit
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">section</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-embed-a-google-maps-location">How to Embed a Google Maps Location</h2>
<p>To the left of the form we will include a Google Maps embedded Google map of where we are located. </p>
<p>We can do so with the help of an online tool: embed-map.com. All you have to do is just enter your location and hit "Generate HTML code". </p>
<p>In the code we are given, don't copy all of the code, just the <code>src</code> attribute from the iframe element. We will replace that value with the default <code>src</code> value we have for our iframe.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/portfolio-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To send over any submitted form data to Netlify, Netlify Forms needs to recognize a form as static HTML. Because our React app is controlled by JavaScript and doesn't consist of plain HTML, we need to add a hidden form to our index.html file in the public folder.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- public/index.html --&gt;</span>

<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-comment">&lt;!-- head content skipped --&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">form</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">netlify</span> <span class="hljs-attr">netlify-honeypot</span>=<span class="hljs-string">"bot-field"</span> <span class="hljs-attr">hidden</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">name</span>=<span class="hljs-string">"name"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">noscript</span>&gt;</span>You need to enable JavaScript to run this app.<span class="hljs-tag">&lt;/<span class="hljs-name">noscript</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"root"</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">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>We need to hide this form, because it doesn't need to be seen by the user, just Netlify. </p>
<p>We'll give it the attribute of <code>hidden</code> as well as a <code>name</code> attribute that matches the JSX form in Contact.js. We also need to give it the <code>netlify</code> attribute so that Netlify Forms recognizes it. Finally, we need to include all of the same inputs as our JSX form: for name, email, message.</p>
<h2 id="heading-how-to-submit-the-contact-form">How to Submit the Contact Form</h2>
<p>Once that's done, we'll head back to Contact.js. We're going to use JavaScript in order to submit this form. </p>
<p>First of all, we're going to create some dedicated state for each of the values that are typed in the form for name, email, and message:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> [name, setName] = React.useState(<span class="hljs-string">""</span>);
<span class="hljs-keyword">const</span> [email, setEmail] = React.useState(<span class="hljs-string">""</span>);
<span class="hljs-keyword">const</span> [message, setMessage] = React.useState(<span class="hljs-string">""</span>);
</code></pre>
<p>We will store what the user types in to each of the inputs in state with the help of the <code>onChange</code> handler.</p>
<p>To handle submission of the form, we will add the <code>onSubmit</code> prop to it. The function that will be called, <code>handleSubmit</code>, will make a post request to the endpoint "/" with all of our form data.</p>
<p>We will set the headers of the request to indicate that we are sending over form data. For the request body, we will include the form name as well as all of the form data from the <code>name</code>, <code>email</code>, and <code>message</code> state variables.</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Contact.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<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">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [name, setName] = React.useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [email, setEmail] = React.useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [message, setMessage] = React.useState(<span class="hljs-string">""</span>);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encode</span>(<span class="hljs-params">data</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(data)
      .map(
        <span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> <span class="hljs-built_in">encodeURIComponent</span>(key) + <span class="hljs-string">"="</span> + <span class="hljs-built_in">encodeURIComponent</span>(data[key])
      )
      .join(<span class="hljs-string">"&amp;"</span>);
  }

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleSubmit</span>(<span class="hljs-params">e</span>) </span>{
    e.preventDefault();
    fetch(<span class="hljs-string">"/"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/x-www-form-urlencoded"</span> },
      <span class="hljs-attr">body</span>: encode({ <span class="hljs-string">"form-name"</span>: <span class="hljs-string">"contact"</span>, name, email, message }),
    })
      .then(<span class="hljs-function">() =&gt;</span> alert(<span class="hljs-string">"Message sent!"</span>))
      .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> alert(error));
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto flex sm:flex-nowrap flex-wrap"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-2/3 md:w-1/2 bg-gray-900 rounded-lg overflow-hidden sm:mr-10 p-10 flex items-end justify-start relative"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">iframe</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">height</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">"map"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0"</span>
            <span class="hljs-attr">frameBorder</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginHeight</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginWidth</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">filter:</span> "<span class="hljs-attr">opacity</span>(<span class="hljs-attr">0.7</span>)" }}
            <span class="hljs-attr">src</span>=<span class="hljs-string">"https://www.google.com/maps/embed/v1/place?q=97+warren+st+new+york+city&amp;key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-900 relative flex flex-wrap py-6 rounded shadow-md"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                ADDRESS
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                97 Warren St. <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
                New York, NY 10007
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6 mt-4 lg:mt-0"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                EMAIL
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-indigo-400 leading-relaxed"</span>&gt;</span>
                reedbarger@email.com
              <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs mt-4"</span>&gt;</span>
                PHONE
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed"</span>&gt;</span>123-456-7890<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</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">form</span>
          <span class="hljs-attr">netlify</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span>
          <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/3 md:w-1/2 flex flex-col md:ml-auto w-full md:py-8 mt-8 md:mt-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white sm:text-4xl text-3xl mb-1 font-medium title-font"</span>&gt;</span>
            Hire Me
          <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed mb-5"</span>&gt;</span>
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illum
            suscipit officia aspernatur veritatis. Asperiores, aliquid?
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Name
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">"name"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setName(e.target.value)}
            /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Email
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setEmail(e.target.value)}
            /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
              <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Message
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 h-32 text-base outline-none text-gray-100 py-1 px-3 resize-none leading-6 transition-colors duration-200 ease-in-out"</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setMessage(e.target.value)}
            /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"</span>&gt;</span>
            Submit
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>As you can see above, we are encoding the form data with a special <code>encode</code> function that you see here. </p>
<p>If the message is sent correctly, we will display an alert that says "Message sent". Otherwise if there is an error, we are going to alert the user of that error.</p>
<h2 id="heading-how-to-build-the-navbar-component">How to Build the Navbar Component</h2>
<p>The last step is to build out our Navbar component. </p>
<p>We want this navbar to stick to the top of our app on large devices and not be sticky on mobile devices. </p>
<p>Additionally, we want to include links to each of our relevant sections for our project skills testimonials and our contact form:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Navbar.js</span>

<span class="hljs-keyword">import</span> { ArrowRightIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<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">Navbar</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-800 md:sticky top-0 z-10"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-medium text-white mb-4 md:mb-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#about"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-3 text-xl"</span>&gt;</span>
            Reed Barger
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</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">nav</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:mr-auto md:ml-4 md:py-1 md:pl-4 md:border-l md:border-gray-700    flex flex-wrap items-center text-base justify-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#projects"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-5 hover:text-white"</span>&gt;</span>
            Past Work
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#skills"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-5 hover:text-white"</span>&gt;</span>
            Skills
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#testimonials"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-5 hover:text-white"</span>&gt;</span>
            Testimonials
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"</span>&gt;</span>
          Hire Me
          <span class="hljs-tag">&lt;<span class="hljs-name">ArrowRightIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-4 h-4 ml-1"</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">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span></span>
  );
}
</code></pre>
<p>How does this stick to top of the page on a larger device? With the help of the class <code>md:sticky</code> on our <code>header</code> element. </p>
<p>This class means that it will have the style rule <code>position: sticky;</code> applied starting on a medium-sized breakpoint (768px).</p>
<h2 id="heading-how-to-deploy-your-portfolio">How to Deploy Your Portfolio</h2>
<p>Now to make our portfolio live, we need to push our application to GitHub. </p>
<p>If you're not familiar with Git and GitHub, I would take a little while just to learn how to push your code to your GitHub account for the first time. It's an essential skill for any developer to know.</p>
<p>Once you're familiar with this process, we can first create a new Github repository. After that, we will run <code>git add .</code>, <code>git commit -m "Deploy"</code>, create our git remote, and <code>git push -u origin master</code>. </p>
<p>Once our project is on GitHub, we can head over to Netlify and select the option "Choose Site from Git". Then we will choose GitHub for our continuous deployment, and pick the GitHub repository to which we just pushed our code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/portfolio-3-min.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After that, our project will be automatically deployed to the web!</p>
<h2 id="heading-whats-next">What's Next</h2>
<p>Congratulations! You now have a portfolio app live on the web that shows off all of your projects and skills to potential employers.</p>
<p>The next step to take would be to set up a custom domain, preferably with your name (i.e. <a target="_blank" href="https://reedbarger.com/">reedbarger.com</a>). Since Netlify includes a DNS you can easily set up a custom domain with them.</p>
<p>Look into maybe adding a blog to your React app to show off even more of your developer knowledge to potential employers.</p>
<p>Make your personal portfolio an expression of yourself and what you are passionate about as a developer and you'll have success!</p>
<h2 id="heading-become-a-professional-react-developer">Become a Professional React Developer</h2>
<p>React is hard. You shouldn't have to figure it out yourself.</p>
<p>I've put everything I know about React into a single course, to help you reach your goals in record time:</p>
<p><a target="_blank" href="https://www.thereactbootcamp.com"><strong>Introducing: The React Bootcamp</strong></a></p>
<p><strong>It’s the one course I wish I had when I started learning React.</strong></p>
<p>Click below to try the React Bootcamp for yourself:</p>
<p><a target="_blank" href="https://www.thereactbootcamp.com"><img src="https://reedbarger.nyc3.digitaloceanspaces.com/reactbootcamp/react-bootcamp-cta-alt.png" alt="Click to join the React Bootcamp" width="600" height="400" loading="lazy"></a>
<em>Click to get started</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a Portfolio with Vue.js Axios, the GitHub REST API, and Netlify ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific In this free book, we will build two simple projects and deploy them on Netlify. We will use Vue.js as our front-end framework, and use different technologies to build our projects.  If you follow this tutorial to the end, you will b... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-portfolio-with-vuejs/</link>
                <guid isPermaLink="false">66d45edcb3016bf139028d31</guid>
                
                    <category>
                        <![CDATA[ axios ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 12 May 2021 20:53:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/05/VUE.JS-_-Article-Cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>In this free book, we will build two simple projects and deploy them on Netlify. We will use Vue.js as our front-end framework, and use different technologies to build our projects. </p>
<p>If you follow this tutorial to the end, you will build a simplified version of Twitter and a single page application for a portfolio using the GitHub API.</p>
<h2 id="heading-what-you-need-to-know-to-follow-this-tutorial">What you need to know to follow this tutorial</h2>
<p>To follow along, you will need at least some basic knowledge of HTML, CSS, and JavaScript. </p>
<p>Knowledge of Vue.js isn't required, as you will learn the basics first and then we'll move into building the projects together.</p>
<p>At the end of each section, you'll find that information in video form via a YouTube link/embed. That way you can watch the videos to cement your knowledge of what you just read.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-introduction">Introduction</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-vue">How to Install Vue</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-vue-instance">How to Create a Vue Instance</a></li>
<li><a class="post-section-overview" href="#heading-how-to-work-with-templates-in-vue">How to Work with Templates in Vue</a></li>
<li><a class="post-section-overview" href="#heading-vue-directives">Vue Directives</a></li>
<li><a class="post-section-overview" href="#heading-methods-in-vue">Methods</a></li>
<li><a class="post-section-overview" href="#heading-conditionals-in-vue-v-ifv-else-ifv-elsev-show">Conditionals</a></li>
<li><a class="post-section-overview" href="#heading-loops-in-vue">Loops</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-user-input-with-event-handling-v-on-in-vue">How to handle user inputs with events Handling</a></li>
<li><a class="post-section-overview" href="#heading-two-way-model-binding-v-model-in-vue">Two way model binding (v-model)</a></li>
<li><a class="post-section-overview" href="#heading-computed-properties-and-methods">Computed Properties and methods</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-simple-twitter-clone"><strong>Project</strong>: Simple Twitter Clone</a></li>
<li><a class="post-section-overview" href="#heading-vue-component-basics">Component basics</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-your-simpletwitter-project-with-components">Project Update: Simple Twitter clone with components</a></li>
<li><a class="post-section-overview" href="#heading-how-to-perform-api-calls-with-axios">Axios and RestAPI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-routing-with-vuerouter">Routing with VueRouter</a></li>
<li><a class="post-section-overview" href="#heading-final-project-how-to-build-a-portfolio-with-vuejs-vuerouter-axios-github-api-and-deploy-to-netlify"><strong>Final Project</strong>: build a Portfolio with VueJS, VueRouter, Axios, GitHub API</a></li>
<li><a class="post-section-overview" href="#heading-continuos-deployment-with-bitbucket-and-netlify"><strong>Deploy</strong> Continuous deployment with BitBucket and Netlify</a></li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>VueJS is a JavaScript framework that has become really popular in recent years. </p>
<p>In this guide, we will start by looking at the fundamentals first, with a quick look at two libraries: VueRouter and Axios. We will use them to build a cool portfolio project at the end.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/CzgP6GamIMc" 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>Click to view the video on <a target="_blank" href="https://youtu.be/CzgP6GamIMc">YouTube</a>.</p>
<h2 id="heading-how-to-install-vue">How to Install Vue</h2>
<p>You can use Vue in your projects by installing it using a package manager like NPM or by using its CDN. If you've never used Vuejs before, I suggest that you use the CDN, as it will be easier if you want to code along with me. </p>
<p>Click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/1-installation/">Repository</a></p>
<p>Click to view the <a target="_blank" href="https://youtu.be/enz0Vi3NuDA">YouTube-Video</a> or find it at the end of this section to reinforce what you've learned.</p>
<h3 id="heading-the-vue-cdn">The Vue CDN</h3>
<p>For the CDN, we only need to include the script tag below inside our HTML file:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Development version for prototyping and learning --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Alternatively, you can use a production-ready script that uses a specific stable release, like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Production version --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>In production, Vue suggests using the optimized version to replace vue.js with vue.min.js.</p>
<p>There is also an ES Modules-compatible build:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js'</span>
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-install-vue-via-npm">How to Install Vue via NPM</h3>
<p>If you plan to build large scale applications, I recommend installing via NPM like this:</p>
<pre><code class="lang-bash">npm install vue
</code></pre>
<p>As I said above, we will use the Vue CDN so that anyone can follow this guide. So our final HTML file will look something like this: </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">title</span>&gt;</span>VueJS Tutorial<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- vue development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.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">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">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.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>Let's break this code down. First, we've add a basic markup for an HTML file. Then we've included the script tag for the VueJs framework. </p>
<p>In the end, before closing the body tag, we've added our main.js script where we placed all the JavaScript code for our application. </p>
<p>Let's now move to the next step and add our first Vue instance inside the main.js file.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/enz0Vi3NuDA" 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-how-to-create-a-vue-instance">How to Create a Vue Instance</h2>
<p>Once you've installed Vue or included it via its CDN, you can create a Vue instance. You can do that using the <code>new Vue()</code> function. This function accepts an object of options.</p>
<p>If you read the documentation, you will see that the vue instance is often stored inside a variable called <code>vm</code>, but you can call it anything you like. I'll call it <code>app</code> during this guide.</p>
<p>So now, inside the main.js file you need to create a variable and store in it the Vue instance like so:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options goes here</span>
})
</code></pre>
<p>The object you pass to the Vue instance is called the options object.
Inside the options object, you can add all the options described in the Vue API reference pages to build our application. </p>
<p>The options object has properties divided into multiple sections: </p>
<ul>
<li>Data </li>
<li>DOM </li>
<li>Life Cycle Hooks </li>
<li>Assets</li>
<li>Composition </li>
<li>Misc categories </li>
</ul>
<p>The first property that you need to build you Vue application is used to connect Vue with a root DOM element. Then you will need some data options to work with.</p>
<p>Let's start by connecting the Vue instance with a root DOM element.</p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/2-create-vue-instance/">Repository</a> here.</p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/gBJaL7Jqh4w">YouTube-Video</a> or find it at the end of this section so you can review what you've learned.</p>
<h3 id="heading-optionsdom-how-to-select-the-root-dom-element">Options/DOM: How to select the root DOM element</h3>
<p>The Options/DOM API gives you an <code>el</code> property that you can use to select an existing DOM element that Vue will use to mount your application instance. </p>
<p>The <code>el</code> property accepts a string that contains a CSS selector for the element or directly a DOM element.</p>
<p>NOTE: Vue discourages using the body or HTML tags and suggests using a different element as a mounting point.</p>
<p>Let's do it. Inside the body of the index.html file, you need to put the following code:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now you have a root element that you can use to connect the Vue instance.
Back inside the main.js file, let's select this element inside the options object. </p>
<p>You can now use the <code>el</code> property to select the element you created with an id of <code>app</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
})
</code></pre>
<p>You now have an element to work with. You can move on to the next step and add to the options object the data object.</p>
<p>You can read more about it in the documentation here:[https://vuejs.org/v2/api/#Options-DOM]</p>
<h3 id="heading-optionsdata-how-to-add-the-data-object-or-function-when-used-in-a-component">Options/Data: How to add the data object (or function when used in a component)</h3>
<p>When a new instance is created, it adds all properties found in its data object to the Vue reactivity system. And when a value in the data object changes, the view will reflect these changes. This is at the base of the VueJS reactivity system. </p>
<p>To explain it, let's see a practical example.</p>
<h4 id="heading-create-a-data-object">Create a data object</h4>
<p>Inside the main.js file you can create a data property that has an object as its value, like so:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {}
})
</code></pre>
<p>The data object can be defined directly inside the Vue instance like in the code above, or outside the instance like in the code below. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> dataObject = {}
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: dataObject
})
</code></pre>
<p>You can pick the one you like.</p>
<h4 id="heading-add-properties-to-the-data-object">Add properties to the Data Object</h4>
<p>Since VueJs is a JavaScript framework, it's helpful to remember that what you know about JavaScript is still valuable here. </p>
<p>Vue is just a JavaScript object that has a number of methods and properties that you can use to simplify and speed up your workflow.</p>
<p>Let's add some properties to the data object to see how it works.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create a data object</span>
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">"#app"</span>,
    <span class="hljs-comment">// create a vue instance, add the data property and the dataObject created</span>
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">alert</span>: <span class="hljs-string">"This is an alert message! "</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ];
    }
})
</code></pre>
<p>With the code above, you simply add two properties to the data object: an <code>alert</code> property and a  <code>projects</code> property. </p>
<p>The alert property just a string while the projects property is an array of objects. </p>
<p>Now that you have some data to work with, let's see how you can access and modify their values.</p>
<h4 id="heading-manipulate-properties-in-the-data-object">Manipulate properties in the data object</h4>
<p>You can access and manipulate the properties of a data object using the variable that contains the Vue instance <code>app</code>. Then you can reference the properties using dot notation, like <code>app.alert</code>. </p>
<p>In the browser, if you open the console you can see that when you write <code>app</code> you get the Vue instance object. So, like any other object with dot notation, you get its properties and methods.</p>
<p>Let's try this out inside the console:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Access the alert property in the data object</span>
app.alert <span class="hljs-comment">// This is an alert message!</span>
<span class="hljs-comment">// update a data property value</span>
app.alert = <span class="hljs-string">"This is a new alert message!"</span> 
app.projects
</code></pre>
<p>The code above does three simple things:</p>
<ul>
<li>the first line accesses the <code>alert</code> property and prints its content "this is an alert message"</li>
<li>the second line assigns a new value to the <code>alert</code> property with the equals operator</li>
<li>finally, the third line returns the value of the projects array. </li>
</ul>
<p>You can also access the entire data object using the shortcuts $data or _data</p>
<p>Back in the console:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Access the entrie data object</span>
app.$data <span class="hljs-comment">// {__ob__: Observer} option 1</span>
app._data <span class="hljs-comment">// {__ob__: Observer} option 2</span>
</code></pre>
<p>You can read more about this in the documentation here: [https://vuejs.org/v2/api/#Options-Data]</p>
<h3 id="heading-options-data-methods">Options Data Methods</h3>
<p>The Vue instance gives you access to a number of properties and methods.
You can access default methods and properties using the <code>$</code> sign. It is used to differentiate Vue defined methods from those defined by the user.</p>
<p>There are a number of instance methods and properties predefined and split into four different categories:</p>
<ul>
<li>Instance Properties</li>
<li>Instance Methods / Data</li>
<li>Instance Methods / Events</li>
<li>Instance Methods/life cycle hooks</li>
</ul>
<p>For instance, with the following code, you can get the <code>data</code> and <code>options</code> objects or access the <code>watch</code> or the <code>on</code> methods. </p>
<pre><code class="lang-js">app.$data <span class="hljs-comment">// returns the data object</span>
app.$options <span class="hljs-comment">// returns the options object</span>
app.$watch() <span class="hljs-comment">// function that watched for changes on the vue instance</span>
app.$on() <span class="hljs-comment">// listen for a custom event on the vue instance</span>
</code></pre>
<p>I won't dive deeper into this since it's out of the scope of this guide. But if you are interested and want to learn more, here is the <a target="_blank" href="https://vuejs.org/v2/api/#Instance-Properties">documentation</a>.</p>
<h3 id="heading-lifecycle-hooks">Lifecycle Hooks</h3>
<p>Vue gives you access to a series of functions called lifecycle hooks. They allow you to run code at specific stages of the Vue initialization steps.</p>
<p>Inside all lifecycle hooks you have access to their <code>this</code> variable that points to the Vue instance. </p>
<p>You will see how this works in more detail in future sections. But for now this is a short summary of the available hooks and what they let you do:</p>
<ul>
<li>beforeCreate (you can run code before the Vue instance is created)</li>
<li>created (you can run code after the Vue instance is created )</li>
<li>beforeMount (you can run code before your element is mounted to the DOM)</li>
<li>mounted (you can run code when the element is mounted to the DOM)</li>
<li>beforeUpdate (you can run code before values are updated in the DOM)</li>
<li>updated (you can run code after values in the DOM have been updated)</li>
<li>beforeDestroy (you can run code before an instance is destroyed)</li>
<li>destroyed (you can run code when an instance is destroyed)</li>
</ul>
<p>During the course, we will often use the mounted hook. If you are curious to learn more about this topic, I suggest you look at the diagram in the documentation first. Find the lifecycle Hooks <a target="_blank" href="https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram">diagram</a> here.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gBJaL7Jqh4w" 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-how-to-work-with-templates-in-vue">How to Work with Templates in Vue</h2>
<p>VueJS uses mustache syntax <code>{{ }}</code> to render data from the Vue instance inside the HTML element. </p>
<p>Using this syntax you can grab properties and methods defined in the Vue instance. The property is then parsed and rendered to the page.</p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/3-work-with-templates/">Repository here</a>.</p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/pDj3SQ8TNzs">YouTube-Video here</a> here, or find it at the end of this section to review what you've just learned.</p>
<h3 id="heading-text-data-binding">Text Data binding</h3>
<p>This is called text data binding. Let's see an example of how you can bind data between the Vue instance and your template file. </p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{ title }}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The code above has an <code>h1</code> tag inside the root element with an id of <code>app</code> we defined in the previous chapter. </p>
<p>Inside the <code>h1</code> tag you use the double curly brackets syntax to render onto the page the value of a property in the data object called that you called <code>title</code>. </p>
<p>You don't have a <code>title</code> property yet inside your data object, so let's add it.</p>
<p>Inside the main.js file</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ]
    }
})
</code></pre>
<p>Now, with the code above you can render the content of the property <code>title</code> inside the <code>h1</code> tag in your template. The final result will be something like this: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>However, with this method, you can only pass a string. If you want to use HTML tags inside the string these will not be parsed but instead will be shown as simple strings.</p>
<p>For instance if you assign the following string to the <code>title</code> property</p>
<pre><code class="lang-js">    title: <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>
</code></pre>
<p>And then try to render it inside our HTML like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>The property <code>title</code> will be rendered as a plain string including the HTML tags 
ie. <code>John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;</code></p>
<p>Of course you can parse HTML too.</p>
<h3 id="heading-how-to-parse-raw-html">How to parse raw HTML</h3>
<p>To render a raw HTML element we need to introduce another important Vue concept called directives. </p>
<p>In this case, you will use the v-html directive inside your HTML tag as an attribute and pass to it the property title. </p>
<p>When you're using Vue directives, the text inside the quotes is considered a JavaScript expression. This means that it's computed and its result is rendered.  </p>
<p>Let's create a separate property for the title with HTML tags inside so that you can see how both render onto the page. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe Portfolio"</span>, 
        <span class="hljs-attr">titleHTLM</span> : <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ]
    }
})
</code></pre>
<p>Now inside your HTML file, you will use this <code>{{}}</code> syntax to render the property <code>title</code>. But on the tag where you want to render raw HTML, with the <code>titleHTML</code> property, you use the v-html directive instead.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{ title }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-html</span>=<span class="hljs-string">"titleHTML"</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">div</span>&gt;</span>
</code></pre>
<p>Both elements will now render correctly including the second property that has HTML tags inside.</p>
<p>NOTE: Rendering HTML can expose XSS vulnerabilities. Never use this approach on user-provided content.</p>
<p>Now that you know how to render data onto the page, let's dig deeper into directives. </p>
<p>If you want to read more, visit the documentation 
<a target="_blank" href="https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions">here</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pDj3SQ8TNzs" 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-vue-directives">Vue Directives</h2>
<p>Inside your HTML files, you can use directives to interact with HTML attributes. A directive applies effects to the DOM when its expression changes. </p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/4-Directives/">Repository here</a></p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/LICvNmhsTEs">YouTube-Video here</a>, or you can find it at the end of this section to review what you've learned.</p>
<h3 id="heading-the-v-bind-directive-on-html-attributes">The v-bind directive on HTML attributes</h3>
<p>So far, you have used the <code>{{}}</code> syntax to render something between HTML opening and closing tags. But inside an HTML tag you cannot use the {{ }} syntax. </p>
<p>So how do you connect an HTML attribute to the Vue instance? You use the v-bind directive instead which lets you access data object properties as you have done before. </p>
<p>The v-bind directive is one of the directives that take arguments which are specified after the colon. In our case here, what's specified after the colon is the HTML attribute name like id, class, href, src and so on. </p>
<p>If you need to dynamically assign an attribute like href or even a class, you can bind it with the Vue instance using the v-bind directive. It will then be able to get what's in the options object, like properties in the data object.</p>
<p>Let's see v-bind in action and start connecting the <code>id</code> and <code>class</code> attributes so you can assign them values dynamically with Vue.</p>
<p>Inside our index.html file:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-bind:class</span>=<span class="hljs-string">"dynamicClass"</span> <span class="hljs-attr">v-bind:id</span>=<span class="hljs-string">"dynamicId"</span>&gt;</span>Dinamically assign a class and an id to the div<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break the code above and see what it's doing. </p>
<p>First, you have a <code>div</code> tag inside the root element. Then you use the v-bind directive on the class and the id attributes. </p>
<p>Inside the quotes, you specify two properties that later you'll define inside the data object of your Vue instance.</p>
<p>Remember that when using Vue directives the content between quotes is treated as a JavaScript expression.</p>
<p>Let's define these two properties inside the Vue instance.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe Portfolio"</span>, 
        <span class="hljs-attr">titleHTLM</span> : <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],
        <span class="hljs-attr">dynamicId</span> : <span class="hljs-string">"projects_section"</span>,
        <span class="hljs-attr">dynamicClass</span> : <span class="hljs-string">"projects"</span>
    }
})
</code></pre>
<p>So you have defined <code>dinamicId: "projects_section"</code> and <code>dynmicClass: "projects"</code> properties and assigned them two values.</p>
<p>Thanks to the data binding on the attributes, your HTML tag will be rendered as follows (and you can now dynamically change your attributes values and see them change reactively):</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"projects_section"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span>&gt;</span>Dynamically assign a class and an id to the div<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-v-bind-with-boolean-values">V-bind with Boolean values</h3>
<p>With attributes using a boolean value, the v-bind directive works differently. It will show the attribute only if the property's value is true. In all other cases, it won't render the attribute and its content. </p>
<p>For the next example, you will use a button with the disabled attribute.</p>
<p>Inside your root HTML element:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">v-bind:disabled</span>=<span class="hljs-string">"disabled"</span>&gt;</span>You can't click this button<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside a Vue instance:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    <span class="hljs-comment">//disabled: false, // wont render the attribute</span>
    <span class="hljs-comment">//disabled: null, // wont render the attribute</span>
    <span class="hljs-comment">//disabled: undefined, // wont render the attribute</span>
    <span class="hljs-attr">disabled</span>: <span class="hljs-literal">true</span> <span class="hljs-comment">// renders the attribute</span>
}
})
</code></pre>
<p>Only if the disabled property is set to true does the attribute become visible and render its property content.</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">disabled</span>&gt;</span>You can't click this button<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>This is something to keep in mind when working with such attributes.</p>
<p>Another thing to consider is that bindings can include a single JavaScript expression, with some restrictions:</p>
<ul>
<li>only expressions are allowed</li>
<li>only a single expression</li>
<li>no statements</li>
<li>no flow control tools, but the ternary operator works.</li>
</ul>
<p>If you want to read more, visit the documentation 
here:[https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions]</p>
<p>So far we have seen only two Vue directives, v-html and v-bind. But there are a number of directives available, and here are some more (to list just a few):</p>
<ul>
<li>v-html</li>
<li>v-bind</li>
<li>v-if</li>
<li>v-else-if</li>
<li>v-else</li>
<li>v-for</li>
<li>v-on
All directives have a v- prefix, but there is shorthand for v-bind (:) and v-on (@). </li>
</ul>
<p>They work in the same way. Here's a quick reference for the v-bind and v-on directives: </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Long syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:href</span>=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Long syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Shorthand for v-on</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Long syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Long syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> @<span class="hljs-attr">:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Now let's see what dynamic arguments are and how they work.</p>
<h3 id="heading-dynamic-arguments-in-vue">Dynamic arguments in Vue</h3>
<p>Directives have been able to have dynamic arguments since Vue 2.6.0. You can use a JavaScript expression in the directive argument if you wrap it inside square brackets.</p>
<p>But there are some restrictions:</p>
<ul>
<li>expressions should evaluate to a string</li>
<li>spaces and quotes are invalid</li>
</ul>
<p>Let's see a practical example</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Visit my Website<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Inside your data object you can define the directive arguments as if they were properties, where the property value is the name of your HTML attribute like <code>hef</code> in the following example:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">attribute_name</span>: <span class="hljs-string">'href'</span>,
        <span class="hljs-attr">url</span>: <span class="hljs-string">'https://fabiopacifici.com'</span>
    }
})
</code></pre>
<p>The code above renders the attribute name <code>href</code> and its value dynamically when you bind it using the v-bind directive.</p>
<p>The result will be this:  </p>
<pre><code class="lang-htm">&lt;a href="https://fabiopacifici.com"&gt;Visit my Website&lt;/a&gt;
</code></pre>
<h3 id="heading-dynamic-events-in-vue">Dynamic events in Vue</h3>
<p>You can apply the same concept to event directives like v-on. This directive does the job of the JavaScript event listener. </p>
<p>v-on accepts an argument like click, for example <code>v-on:click="doSomething"</code>.</p>
<p>To apply the concept of dynamicity, let's create a v-on directive and use the square brackets after it to specify a dynamic event.</p>
<p>Inside the index.html file you will place the following code:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:</span>[<span class="hljs-attr">event_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break down the code above. </p>
<p>First you have your root element, the <code>div</code> with an <code>id</code> of <code>app</code>. Inside the root element you add an anchor tag <code>&lt;a&gt;Some link&lt;/a&gt;</code>. </p>
<p>The anchor tag has a <code>v-on</code> directive in it. After the directive you specify a dynamic argument <code>v-on:[event_name]</code> where <code>event_name</code> will be a property inside your Vue instance that you can change as you need. </p>
<p>The v-on directive works like any event listener, so between quotes you need to specify the name of the function that you want to run when the event is triggered, therefore <code>runFunction</code>.</p>
<p>Now, inside your main.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">event_name</span>: <span class="hljs-string">"click"</span>
    },
    <span class="hljs-attr">methods</span>: {
        runFunction() {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"test click function"</span>);
        }
    }
})
</code></pre>
<p>Let's review what the code above does. </p>
<p>First, you create the Vue instance. Then you add the <code>event_name</code> property inside the data object and you assign to it a value of <code>click</code>. This is the event you will listen for.</p>
<p>Finally, we said that the v-on directive runs a function when the event is triggered, therefore you need to write a method inside your Vue instance. So inside the methods object, create a new function called <code>runFunction</code> that will simply output a message inside the console. </p>
<p>The power of dynamic events is clear when you replace the value of the <code>event_name</code> property with a different event name.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/LICvNmhsTEs" 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-methods-in-vue">Methods in Vue</h2>
<p>So far we've learned how to bind data using the v-bind directive inside your template. In the next section, you will learn more directives – but before diving into that, let's quickly talk about how to store your functions. </p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/5-Methods/">Repository here</a> </p>
<p>And you can see the video version of this section on <a target="_blank" href="https://youtu.be/dESmaEvkZ2I">YouTube</a> if you want to review what you learn.</p>
<p>Since you are working in a big object, the Vue instance function will take the name of the methods. And as you might guess, the Options object has a property called <code>methods</code> where you can store your functions as you do for your data. </p>
<p>Inside your Vue instance, define a method that you can call anything you like – just remember to use a naming convention that clearly describes your code.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">firstName</span>: <span class="hljs-string">"Fabio"</span>,
        <span class="hljs-attr">lastName</span>: <span class="hljs-string">"Pacific"</span> 
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// es6 syntax</span>
        getFullName(){
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.firstName + <span class="hljs-string">" "</span> + <span class="hljs-built_in">this</span>.lastName;
        }
        <span class="hljs-comment">// es5 syntax</span>
    <span class="hljs-comment">/* getFullName: function(){

        } */</span>
    }
});
</code></pre>
<p>In the code above, you created a method inside the methods object. You called it <code>getFullName</code>. Inside a method, you have access to the <code>this</code> keyword that refers to the object instance, so you can use it to access from a method the properties stored in the data object.</p>
<p>When you call the method <code>getFullName</code> the method will return a single string that contains both the first and the last name. </p>
<p>Now inside your HTML file, you can simply call the method as you did when you needed to access properties in the data object <code>{{ getFullName() }}</code> </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{ getFullName() }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that you know how to create a method and where to put it in the Vue instance, let's move forward and learn more about directives.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/dESmaEvkZ2I" 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-conditionals-in-vue-v-ifv-else-ifv-elsev-show">Conditionals in Vue (v-if/v-else-if/v-else/v-show)</h2>
<p>Now it's time to learn more about directives. We will start by looking at how conditionals work in VueJS. The first directive of this section is the <code>v-if</code>, which allows you to render blocks of code based on a certain condition. </p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/6-Conditionals/]">Repository here</a>.</p>
<p>And you can click to view the video on <a target="_blank" href="https://youtu.be/VNaCsloA1ZU">YouTube here</a> or use it to review at the end of the section.</p>
<p>Like the <code>if-else</code> statements in vanilla JavaScript, the v-if will check if the returned value of a conditional expression evaluates to true. If so it will render the HTML element and everything you place inside it. </p>
<p>Since it's a directive, it works on a single element (HTML tag). If you want to extend its behaviour on multiple elements, then you need to wrap them inside a <code>&lt;template&gt;</code> tag.</p>
<p>The v-if directive works in the same way as the v-bind directive works: it has access to the properties in the data object and accepts an expression between its quotes. </p>
<p>If the returned value of the expression or the value of the data property you use evaluates to <code>true</code>, then the directive renders the HTML element. Otherwise it doesn't.</p>
<p>Of course, you can check for multiple conditions and end up rendering an element if none of these evaluates to true. You do that using v-if together with the v-else-if and v-else directives.</p>
<p>Let's see a simple example and write some code inside your main.js file to show or hide an element.</p>
<p>The first thing to note is that if you have a property that returns a boolean value, it is enough to use it inside the v-if directive to show/hide an element, like so: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"showTitle"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>And a vue instance with a showTitle property set to true.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">showTitle</span>: <span class="hljs-literal">true</span>,
    }
})
</code></pre>
<p>In such a case, you are saying show the title property only if the value of <code>showTitle</code> is <code>true</code>. If you change it to false, the title won't show.</p>
<p>You can put a simple expression inside the quotes of a v-if directive that once computed evaluates to a boolean.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt;= 18"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Inside your main.js file</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: <span class="hljs-number">18</span>,
    }
})
</code></pre>
<p>In the code above we wrote an expression on the v-if directive that checks if the <code>age</code> property is greater than or equal to 18. If the result is true then the <code>h2</code> will be shown onto the page.</p>
<p>Now let's move to a more complex example and add another condition using the v-else-if.</p>
<h4 id="heading-v-ifv-else-if">v-if/v-else-if</h4>
<p>In the following example, you will first create a v-if condition similar to the one above – but this time you'll check if the user is over 18 but under 21 using the <code>&amp;&amp;</code> operator. </p>
<p>If true, then you'll show the time with an additional note. If false, and the user is over 21, then we will simply show the title of the movie. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt; 21"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age &gt; 18 &amp;&amp; age &lt; 21"</span>&gt;</span> {{ movieTitle }} | Watch with an adult<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Inside the Vue instance you could have an <code>age</code> property. But to make your simple program dynamic, you can instead use a prompt to ask the user their age. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> userAge = <span class="hljs-built_in">Number</span>(prompt(<span class="hljs-string">"What's your age?"</span>))
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: userAge,
    }
})
</code></pre>
<p>So the code here first asks the user their age, then stores the result as a number inside the variable <code>userAge</code>. </p>
<p>You'll later use the <code>userAge</code> variable inside the data object to assign a value to the <code>age</code> property so that based on its value you will render one element or the other.</p>
<p>Let's move forward and use the v-else directive to show a different message in case the user is under 18.</p>
<h4 id="heading-v-else-directive">v-else directive:</h4>
<p>The <code>v-else</code> directive works differently. You don't have to pass it anything. It simply enters into action when none of the previous conditions evaluate to a true value.</p>
<p>So the new HTML element is fairly simple:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt; 21"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age &gt; 18 &amp;&amp; age &lt; 21"</span>&gt;</span> {{ movieTitle }} | Watch with an adult<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-else</span>&gt;</span> Sorry You are too young to see this movie<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we have a <code>p</code> tag with a v-else directive attached. As you can see, it looks like an attribute without values (like the disabled or required HTML attributes).</p>
<p>Your JavaScript file has not changed.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> userAge = <span class="hljs-built_in">Number</span>(prompt(<span class="hljs-string">"What's your age?"</span>))
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: userAge,
    }
})
</code></pre>
<p>That's all you need to know about conditional rendering to be able to move forward with your first project. But if you want to learn more, here is the documentation: [https://vuejs.org/v2/guide/conditional.html]</p>
<p>You need to learn a few more things before you'll be able to build your first project, which is a simplified Twitter clone. The next topic is about Loops.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VNaCsloA1ZU" 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-loops-in-vue">Loops in Vue</h2>
<p>Let's go back to the previous example and learn how to use the v-for directive to output each project of the array onto the page. </p>
<p>You can click to view the Repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/7-loops/">here</a></p>
<p>And you can click to view the YouTube-Video <a target="_blank" href="https://youtu.be/aViHg80-7Bs">here</a>, or you can find it at the end of this section to review what you've learned.</p>
<p>For our next task, it would be useful if we could use a loop, and the v-for directive is here to help.</p>
<p>Its syntax doesn't have much in common with a classic <code>for</code> loop in JavaScript, but rather with a Python <code>for in</code> loop or with the <code>for in</code> JavaScript loop used to iterate over objects.</p>
<p>With this directive, you specify the elements of the array and the single element between quotes using the syntax <code>project in projects</code>. Here, projects are the property inside the data object that contains an array of objects, and project is the single element of the array. </p>
<p>You can call this as you like, but keep in mind that what follows the <code>in</code> keyword must be an iterable from your data object while what comes before can be anything you like to refer to each element of the iterable. </p>
<p>In your case, project seems the most appropriate choice since you have an array of projects.</p>
<p>Your JavaScript file will look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],

    }

});
</code></pre>
<p>Now inside the HTML file, let's use the v-for to render the title of each project.</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{name}} {{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>{{project.title}}<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>
</code></pre>
<p>In the code above, you used <code>{{name}} {{title}}</code> to render the main title for your portfolio. Then you used the v-for directive and specified inside the quests that you want to assign each element of the iteration to a project variable <code>v-for="project in projects"</code>.</p>
<p>Now on each iteration, the <code>project</code> variable holds an object from which you can retrieve its properties using dot notation like so: <code>{{ project.title }}</code>.</p>
<p>One thing to note is that the v-for directive also gives you access to the index of the element at each iteration. You can store it in a variable as you did with the single element you called project. </p>
<p>To do that, you need to wrap them between parentheses and separate the element and its index with a comma, like so <code>v-for="(project, index) in projects"</code>. </p>
<p>Also, note that when working with objects Vue can show an alert to inform you that the use of a key is recommended. This means that it expects a key to identify each element when it's rendered. </p>
<p>You can do this using the <code>key</code> attribute and bind it, for instance to an id property on the object or to another different property, like so</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{name}} {{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"project.title"</span>&gt;</span>{{project.title}}<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>
</code></pre>
<p>Here you used the v-bind shorthand directive to bind the key attribute to the project.id property if it exists or to another property if not. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],

    }

});
</code></pre>
<p>v-for can also be used to iterate over objects. In such a case, you have access to the value, the key, and also the index like so <code>v-for="(value, key, index) in object"</code> where 'object' is a property in the data object.</p>
<p>If you want to learn more, visit the documentation here: [https://vuejs.org/v2/guide/list.html]</p>
<p>Let's now move on to another important feature of Vue: how to handle user inputs and events. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/aViHg80-7Bs" 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-how-to-handle-user-input-with-event-handling-v-on-in-vue">How to Handle User Input with Event Handling (v-on) in Vue</h2>
<p>To make the application react to a user's input, Vue provides a straightforward directive called <code>v-on</code>. This is one of the directives that accepts arguments, similar to the v-bind directive. </p>
<p>With such a directive, it's easy to listen for events triggered by a user.</p>
<p>The v-on directive lets you run a function that executes a block of code when the user performs an action, like when they click on a button, hover on an element, or press a specific key on the keyboard.</p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/8-user-inputs-events-handling/">Repository here</a>.</p>
<p>And you can view the video on <a target="_blank" href="https://youtu.be/9_U1eagqOJY">YouTube here</a> or at the end of this section to review what you've learned.</p>
<h3 id="heading-v-on-syntax-and-events">V-on Syntax and Events</h3>
<p>There are two types of syntax we can use, the long-form or the short. They're equivalent, so pick the one you prefer. What follows is just a representation of the syntax, and I'll explain it in detail in a minute.</p>
<p>Long syntax: <code>v-on:EventName='doSomething'</code>
Shot syntax: <code>@EventName='doSomething'</code></p>
<p>There are many events you can listen to, such as: </p>
<ul>
<li>click</li>
<li>submit</li>
<li>mouseover</li>
<li>mouseenter</li>
<li>mouseleave</li>
<li>keyup</li>
<li>keydown</li>
<li>keypress</li>
</ul>
<p>But you can also create custom events (which you will see when you reach the components section).</p>
<p>Let's pick the long-form syntax: <code>v-on:EventName='doSomething</code>. I'll explain it more now.</p>
<p>First, you have the directive <code>v-on</code>. Then you have an argument that is the event name you want to listen for, like <code>click</code>. After that, the <code>doSomething</code> can be any method that you have defined inside the methods object of the Vue instance.</p>
<p>This method is like any other function that you define inside a JavaScript object. It can have parameters or not. If it has them, you can call the method and pass parameters to it as usual like this: <code>doSomething(param, param_2, param3)</code>. </p>
<p>You can have something like this <code>&lt;div v-on:click="likeProject"&gt;Like&lt;/div&gt;</code> and when the user clicks on this element, it will trigger a method and run some code to increase a like counter inside a project.</p>
<p>Let's first create the HTML you need for that:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(project,index) in projects"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Like
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"likeProject(index)"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        {{project.likes}}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the code here, first you'll use the v-for directive to loop over the array of projects. Note that you should use the syntax <code>(project, index) in projects</code> because you will need to pass the index to the like method that you defined earlier. </p>
<p>After that, you output some data onto the page (like the project name in uppercase letters) then the description, and a <code>div</code> tag with an icon for the likes (remember to add font awesome to get the icon). </p>
<p>On the heart icon, add the directive v-on using the short syntax <code>@click="likeProject(index)"</code> between quotes that you used to invoke your <code>likeProject(index)</code> method. Then pass to it the index as a parameter so you can find the current project the user clicked on. </p>
<p>Finally, you'll render the likes onto the page for the current project using the <code>{{project.likes}}</code> syntax.</p>
<p>Now it's time to go in the Vue instance and write your method.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
        <span class="hljs-attr">methods</span>: {
            likeProject(index){
                <span class="hljs-keyword">const</span> project = <span class="hljs-built_in">this</span>.projects[index]
                project.likes++
                <span class="hljs-built_in">console</span>.log(project.likes)
            }

    }
});
</code></pre>
<p>As I said earlier, you needed to define a method to call when the user clicks on a link. So you create the <code>likeProject</code> method, which accepts a parameter that will be the index of the element the user clicked on. </p>
<p>You can then add a likes property inside your projects array and access it for the current project to increment its value every time the user clicks on your link.</p>
<h3 id="heading-how-to-access-the-original-event">How to access the original event</h3>
<p>If for any reason you need to access the original DOM event, you could have used the special <code>$event</code> variable inside the method like this on the v-on directive: <code>doSomething(param1, param2, $event)</code>. Let's see an example of that now.</p>
<p>You need to add the special variable in the method call on your v-on directive like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"likeProject(index, $event)"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
</code></pre>
<p>Then you can access the original event inside your method like so: </p>
<pre><code class="lang-js">likeProject(index, event){
    <span class="hljs-built_in">console</span>.log(event); <span class="hljs-comment">// get the original event</span>
    <span class="hljs-keyword">const</span> project = <span class="hljs-built_in">this</span>.projects[index]
    project.likes++
    <span class="hljs-built_in">console</span>.log(project.likes)
}
</code></pre>
<p>Now that you know how the v-on directive works, let's improve our Likes example and put something more in it. We will use key modifiers in the next example, so let's quickly see what they are and what you can do with them.</p>
<h3 id="heading-event-modifiers-in-vue">Event Modifiers in Vue</h3>
<p>With events, Vue provides access to a number of Event modifiers. They are divided into 4 main groups. You can add these modifiers to a directive to change the way your event behaves. They are like postfix and you can chain them using dot notation. </p>
<p>Below there is a quick reference.</p>
<p>Categories:</p>
<ul>
<li>event modifiers</li>
<li>key modifiers</li>
<li>system modifiers keys</li>
<li>mouse buttons modifiers</li>
</ul>
<p>Event Modifiers:
.stop
.prevent 
.capture
.self
.once
.passive</p>
<p>Key Modifiers:
You can add these modifiers to the @keyup listener to listen for when these keys are pressed or use them as a combination with the @click event to listen for a click+space, for example. <code>@click.enter="doSomething"</code></p>
<p>.enter
.tab
.delete (captures both “Delete” and “Backspace” keys)
.esc
.space
.up
.down
.left
.right</p>
<p>System modifiers:
With these modifiers, you can trigger mouse or keyboard event listeners when the corresponding key is pressed.
.ctrl 
.alt
.shift
.meta
.exact (allows control of the exact combination of system modifiers needed to trigger an event)</p>
<p>Mouse buttons modifiers:
These modifiers allow you to trigger a mouse event listener if the corresponding mouse button is clicked. </p>
<p>.left
.right
.middle</p>
<p>If you want to learn more, read the docs <a target="_blank" href="https://vuejs.org/v2/guide/events.html#Event-Modifiers">here</a>.</p>
<h3 id="heading-how-to-like-a-project-with-key-modifiers">How to like a project with key modifiers</h3>
<p>In the previous example, you used the v-on:click directive to trigger a mouse event listener that aimed to simulate a like on a project. </p>
<p>But the user was able to add as many likes they wanted by clicking on the icon. </p>
<p>In the next example, you will do things a bit differently.</p>
<ul>
<li>First, you will prevent the user from adding more than one like to each project,</li>
<li>Then you will let the user remove a like </li>
<li>Finally, you will keep the likes on the page even after the user refreshes the page.</li>
</ul>
<p>Let's get started. This time you will use mouse button modifiers to listen for clicks. The left mouse button click will trigger the add like behaviour and the right mouse button click will trigger the remove behaviour.</p>
<p>Inside your HTML file: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Users can like a project with a left click and dislike it with right click --&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Like 
                <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> 
                    @<span class="hljs-attr">click.left</span>=<span class="hljs-string">"addLike(project)"</span> 
                    @<span class="hljs-attr">click.right</span>=<span class="hljs-string">"removeLike(project, $event)"</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> 
               {{project.likes}}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</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">div</span>&gt;</span>
</code></pre>
<p>In the code above you have taken what you had before and simply added a left mouse button key modifier to the click event <code>@click.left</code>. Then you invoked the <code>addLike</code> method. This will make your project like counter increase by one as we have seen before. </p>
<p>Then you added another event listener to the same element, but this time you used the <code>.right</code> mouse button key modifier to listen when the user clicks on our icon using the right button <code>@click.right="removeLike()"</code>. </p>
<p>In the remove like method, you have also passed the special variable $event so that you can use the original event later in your method to prevent its default behaviour and open the contextual menu. </p>
<p>But we said earlier that you can also chain key modifiers and indeed there is a <code>.prevent</code> key modifier that you can use here instead of the <code>$event</code> variable. You could do the same like this: <code>@click.right.prevent="removeLike(project)"</code> </p>
<p>Let's see how to structure your main.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
    <span class="hljs-attr">methods</span>: {
        addLike(project){
           <span class="hljs-built_in">console</span>.log(project)  
        },
        removeLike(project, event){
            <span class="hljs-built_in">console</span>.log(project)
            <span class="hljs-built_in">console</span>.log(event)
        }
    }

});
</code></pre>
<p>So in the data object, you have a <code>projects</code> property that is an array of objects. Each object has a likes property that you will increment or decrement depending on what mouse button the user clicks.</p>
<p>Inside the <code>methods</code> object, you created the two methods you referenced in your v-on directives <code>addLike()</code> and <code>removeLike()</code>. For now, you are only logging to the console the project parameter value and the event value. You will implement the logic in a minute.</p>
<p>Let's start with the add likes method – it could look like this:</p>
<pre><code class="lang-js">addLike(project){
    <span class="hljs-keyword">const</span> projectTitle = project.title;
    <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
        project.likes++;
        <span class="hljs-built_in">localStorage</span>.setItem(projectTitle, <span class="hljs-literal">true</span>);
    }              
}
</code></pre>
<p>There are a few things going on here. In the first line, you're storing the project title inside the <code>projectTitle</code> variable. Then, you said you wanted data to persist if you refresh the page so you are using the <code>localStorage</code> API to store information inside the client browser. </p>
<p>You increment the likes count by one, but you do that depending on a value inside the local storage. </p>
<p>You can do this by first checking to see if there is a key in the <code>localStorage</code> matching your project title, <code>if(!localStorage.getItem(projectTitle))</code>. </p>
<p>If this evaluates to false, then you will run the code inside the if block and first increment the likes <code>project.likes++</code>. </p>
<p>Second, use the <code>.setItem()</code> method of the local storage API to set a key-value pair with the project title as the key and a boolean value as its value <code>localStorage.setItem(projectTitle, true)</code>.</p>
<p>To put an item in the local storage, you'll use <code>localStorage.setItem()</code>. The set item method accepts a key-value pair. Your key will be the title you saved in the variable <code>projectTitle</code> and the value will the boolean value <code>true</code>.</p>
<p>Now let's see if this works. </p>
<pre><code class="lang-js">removeLike(project, event){
    event.preventDefault(); <span class="hljs-comment">// This can be omitted if we use the prevent key modifier</span>
    <span class="hljs-keyword">const</span> projectTitle = project.title;
    <span class="hljs-keyword">if</span>(project.likes &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
        project.likes--;
        <span class="hljs-built_in">localStorage</span>.removeItem(projectTitle);
    }
}
</code></pre>
<p>This function does the opposite of the previous. When the user clicks their right mouse button, the method <code>removeLikes()</code> is executed and you do the following:</p>
<p>First things first, you need to prevent its default behaviour. Otherwise when the user right-clicks on the icon, the contextual menu will pop up and we don't want that. So, you'll use the <code>event.preventDefault()</code> method on the original event that is represented by the <code>event</code> parameter on your method. </p>
<p>Alternatively, you can omit this if you use the prevent key modifier in the v-on directive <code>@click.right.prevent="removeLike(project)</code>.</p>
<p>The next step is to grab the project title. Since you also passed a parameter to the method to represent the current project object <code>removeLike(project, event)</code>, you can store the project title in a variable <code>projectTitle</code>.</p>
<p>Then you need to make a couple of checks. First, you want to decrement the likes only if its value is greater than zero. Then you want to make sure the project title is in the local storage as a key with a value. </p>
<p>So, in your condition, you have done both checks <code>if(project.likes &gt; 0 &amp;&amp; localStorage.getItem(projectTitle))</code>. Now, if both conditions evaluate to true, the code inside the if block can run. </p>
<p>First you remove the like by decrementing its value <code>project.likes--</code>. Then you remove the project title from the local storage using the <code>removeItem</code> method and pass to it the key you want to remove (which is the project title <code>localStorage.removeItem(projectTitle)</code>).   </p>
<p>To put it all together, you should now have the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
        <span class="hljs-attr">methods</span>: {
        addLike(project)
        {
            <span class="hljs-comment">//console.log(project, "like");</span>
            <span class="hljs-keyword">const</span> projectTitle = project.title;
            <span class="hljs-comment">// check if the current project is not in the local storage</span>
            <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
                <span class="hljs-comment">// set the item in the storage and increase the likes counter</span>
                project.likes++;
                <span class="hljs-built_in">localStorage</span>.setItem(projectTitle, <span class="hljs-literal">true</span>);
            }

        },
        removeLike(project){ 
            <span class="hljs-keyword">const</span> projectTitle = project.title;
            <span class="hljs-built_in">console</span>.log(project, <span class="hljs-string">"dislike"</span>);  
            <span class="hljs-keyword">if</span>(project.likes &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">Boolean</span>(<span class="hljs-built_in">localStorage</span>.getItem(projectTitle))) {
                project.likes--;
                <span class="hljs-built_in">localStorage</span>.removeItem(projectTitle);
            }

        }
    },
    mounted(){
        <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span> {
            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(project.title) !== <span class="hljs-literal">null</span>) {
                project.likes = <span class="hljs-number">1</span>; 
            }
        });
    }

});
</code></pre>
<p>To make the code work, you also need to add a life cycle hook called mounted. This will let you run code when the root element is mounted on the Vue instance. With it, you can check if the localStorage has a key corresponding to your project title and if so, update the value of the likes counter.</p>
<p>And your HTML is still the same:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Users can like a project with a left click and dislike it with right click --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Like 
            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> 
                @<span class="hljs-attr">click.left</span>=<span class="hljs-string">"addLike(project)"</span> 
                @<span class="hljs-attr">click.right</span>=<span class="hljs-string">"removeLike(project, $event)"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> 
            {{project.likes}}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</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">div</span>&gt;</span>
</code></pre>
<p>Remember that you can get rid of the <code>$event</code> variable passed to the <code>removeLike</code> method by using the event key modifier like so: <code>@click.right.prevent="removeLike(project)"</code></p>
<p>You have learned a lot so far! And now that you have seen key modifiers in action, we can move forward to the next topic: two-way model binding and the v-model directive. Then we'll start building our Twitter clone. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9_U1eagqOJY" 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-two-way-model-binding-v-model-in-vue">Two Way Model Binding (v-model) in Vue</h2>
<p>All right, so far we have seen how to bind properties from the data object to our HTML tags and inside attributes, 
how to loop over a sequence of elements and, how to display conditionally elements onto our template with conditionals. </p>
<p>We have covered how to define methods inside the methods object so that we can perform more complex operations on our data and, 
we have learned how to work with events using the v-on directive.</p>
<p>In the next section, we will look at how Vue opens a two-way communication channel between a form's input and, a property defined
inside the data object. Then we will use this knowledge to build our first project together.</p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/9-two-way-binding/">Repositoy here</a>.</p>
<p>And you can view the video on <a target="_blank" href="https://youtu.be/pBUXTUvDRCo">YouTube here</a> or at the end of the section if you want to review what you've learned.</p>
<h3 id="heading-how-does-the-v-model-directive-work">How does the v-model directive work?</h3>
<p>The v-model is another Vue directive. You can use it out of the box, and it's useful to simplify the way an input tag can communicate 
with a Vue instance's property in the data object. </p>
<p>It works like all the other directives. The main difference is that when it's implemented, your application will listen for changes 
inside the input with this v-model directive and update the attached property's value immediately inside the data object and vice-versa. </p>
<p>It is effectively a two-way communication channel between your template and the Vue instance. It's the Vue way to interact with user input and simplify your life as a developer.</p>
<p>Let's look at a straightforward example.</p>
<h3 id="heading-how-to-use-the-v-model-on-an-input-tag">How to use the v-model on an input tag</h3>
<p>First, you need an input tag inside the element you defined as the root element in the Vue instance, the div with an id of <code>app</code>. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">placehoder</span>=<span class="hljs-string">"What's happening today?"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the HTML you have an input tag, which has the v-model directive attached to it as an HTML attribute. Anything between quotes is computed as a JavaScript expression, so you write the name of a property <code>tweet</code> that you will create inside the Vue data object.</p>
<p>So let's do it.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>
    }
});
</code></pre>
<p>So now you have a Vue instance and, in it, you have a data object with a tweet property that has an empty string as its value. </p>
<p>If you open the console and inspect the Vue element, you can see the two-way data binding in action. </p>
<p>By changing the value of the tweet property, you will immediately update the value inside the input tag and vice-versa. </p>
<p>You have this <code>tweet</code> property in the data object and you already know how to render its content onto the page. So now you can update your markup and add a paragraph under the input tag to see the value dynamically changing while you type.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">placehoder</span>=<span class="hljs-string">"What's happening today?"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{tweet}}<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>How cool is that? Now you can see the tweet property changing in real-time as you type. </p>
<p>That's the two-way data binding. If you change the content of the tweet property directly it will be reflected in your template, too.</p>
<p>If you want to learn more, make sure to read the official <a target="_blank" href="https://vuejs.org/v2/guide/forms.html">Documentation</a> too.</p>
<h3 id="heading-how-to-build-a-tweet-box">How to build a tweet box</h3>
<p>Now, let's raise the bar a little and build something together.</p>
<ul>
<li>We will create a simple <code>textarea</code> with a submit button, </li>
<li>We will display the number of characters the user has left while they are typing so that they can submit the form without exceeding the max number of characters allowed. </li>
<li>Like in a tweet, the maximum number of characters will be 200.</li>
</ul>
<h4 id="heading-how-to-define-the-initial-markup">How to define the initial markup</h4>
<p>Now you need to define a markup. So inside our index.html file, write the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
       <span class="hljs-comment">&lt;!-- Code here --&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- More code here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>First you have first created your root element for the Vue instance so that it can monitor the markup and do its magic.</p>
<p>Then you created a form tag with an event listener using the directive v-on, which listens for the submit event and runs a function <code>submitData</code> that you still have to create.</p>
<p>You also added the <code>.prevent</code> modifier so that the page doesn't refresh when you submit the form.</p>
<h4 id="heading-how-to-define-the-vue-instance-and-methods">How to define the Vue instance and methods</h4>
<p>Let's define our Vue instance and create the <code>submitData</code> method so that you can use it later when you need it.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-comment">// data object props here</span>
    },

    <span class="hljs-attr">methods</span>: {
          submitData(){
             <span class="hljs-comment">// Code here</span>
        }
    },

});
</code></pre>
<h4 id="heading-how-to-add-a-text-area-and-a-submit-button">How to add a text area and a submit button</h4>
<p>Now back to the HTML: let's add the text area inside your form. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
       <span class="hljs-comment">&lt;!-- Code here --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- More code here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside the form tag, you create a label and a <code>textarea</code> for your tweet box. 
On the <code>textarea</code> you use the directive v-model to bind the <code>textarea</code> value to a <code>tweet</code> property and vice-versa. So now when one changes the other changes, too. </p>
<p>NOTE: The v-model directive is used on form elements like inputs, text areas, check box, and more.</p>
<p>After the <code>textarea</code> you put a button of type submit so that when a user clicks it the form's data are sent to your application's <code>submitData()</code> method and you can process them.</p>
<h4 id="heading-how-to-add-properties-to-the-vue-instance">How to add properties to the Vue instance</h4>
<p>Now inside your JavaScript file, you need to create the tweet property in the data object and do something with this information so that you can later show a list of tweets sent. </p>
<p>We also said that we want to limit the characters to 200 and show an error when they're in excess.</p>
<p>So let's add a few more properties here like:</p>
<ul>
<li><code>tweet</code> for the current tweet message that the user inputs in the text area</li>
<li><code>tweets</code> for the list of tweets</li>
<li><code>max_length</code> for the characters limit</li>
</ul>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
    },

    <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-comment">/* Handle the tweet */</span>
        }
    },

});
</code></pre>
<p>So now with the tweets property as an array and using the two-day binding between the tweet property and the <code>textarea</code>, you can push all tweets inside the <code>tweets</code> array when the user submits the form by triggering the 
<code>submitData</code> method.</p>
<h4 id="heading-how-to-implement-the-character-counter">How to implement the character counter</h4>
<p>Before implementing the <code>submitData</code> method, you can show a character counter while the user types in the textarea. </p>
<p>Let's implement this feature so the user knows if they can submit the tweet or not.</p>
<p>Back in the HTML file, you can add a div with a couple of span elements and use a v-if directive to check the length of the character. It'll show the counter while the user is within the characters limit, otherwise it'll show an error message.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show character limits here --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">div</span>&gt;</span>
</code></pre>
<p>The code above uses two-way data binding between the tweet property and the <code>textarea</code> to find out if the user has reached the character limit that you defined as the <code>max_lenght</code> property. </p>
<p>Since the tweet property is connected to the <code>textarea</code>, you can use the <code>v-if</code> directive combined with the <code>tweet.length</code> and the <code>max_length</code> properties to make the comparison. </p>
<p>Now, every time the user types something in the <code>textarea</code>, the string saved in the <code>tweet</code> property increases by one character. Then you can use the <code>.length</code> property to see how long the whole string is and compare it against your <code>max_length</code> property.</p>
<p>You use the directive  <code>v-if="tweet.length &lt;= max_length"</code> to make your comparison. When this comparison returns true, the user will see the span tag with its content, the counter. </p>
<p>Inside the span tag, you used the moustache syntax to show the user the current length of the property <code>tweet</code> and the character limit.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<p>After the <code>v-if</code> directive, a <code>v-else</code> directive handles the error message that you show to the user when there are no characters left to use. </p>
<p>Here the content of the span element shows a message that tells how many characters there are in excess by subtracting the tweet length from the <code>max_lenght</code> property.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<h4 id="heading-how-to-submit-the-form">How to submit the form</h4>
<p>All that's left is to add the tweet to the list of tweets and show them on the page when the user submits the form.</p>
<p>Let's complete the <code>submitData</code> method so that every time it's executed it pushes a new object to the tweets array. </p>
<p>Inside the methods object the <code>submitData</code> methods now looks like this:</p>
<pre><code class="lang-js"> submitData(){
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
        <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
        <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
    } 
}
</code></pre>
<p>The method above first checks if the length of the <code>tweet</code> property is less than or equal to the <code>max_length</code> property. If the condition evaluates to true, then you can add the <code>tweet</code> content to the array using the <code>unshift</code> method (which adds it to the beginning of the array).</p>
<p>Finally, you need to clear the value of the <code>tweet</code> property. You can do this by assigning to it an empty string again.</p>
<p>NOTE that since you are inside a method, you need to use the <code>this</code> keyword to grab properties and eventually methods inside the Vue instance.</p>
<h4 id="heading-how-to-show-a-list-of-tweets">How to show a list of tweets</h4>
<p>Now, you can also show a list of tweets in your template. </p>
<p>To do that you will use a <code>v-for</code> directive and loop over the <code>tweets</code> array to show each tweet.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"text in tweets"</span>&gt;</span>{{text}}<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>
</code></pre>
<h3 id="heading-put-it-all-together">Put it all together</h3>
<p>The final code now looks like this:</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">title</span>&gt;</span>VueJs v-model<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">"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Fontawesome CDN --&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">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.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">style</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">style</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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you wanna tweet about today<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Tweet form --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Alert the user  --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"my-3"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span>
                {{ ` Max: ${tweet.length} of ${max_length} characters` }}
            <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">v-else</span>&gt;</span> {{ `Max char limit reached! excess characters: ${max_length -
                tweet.length} ` }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

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

        <span class="hljs-comment">&lt;!-- Tweets message --&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> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"tweet in tweets"</span>&gt;</span>
                {{tweet}}
            <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 class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.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>Our final javascript file </p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>        
    }, 
    <span class="hljs-attr">methods</span>: {
        submitData(){
            <span class="hljs-comment">// Handle the tweet submission</span>
            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length){
                <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
            }
        }
    }
})
</code></pre>
<h3 id="heading-improvements-you-can-make">Improvements you can make</h3>
<p>If you take this bit of code from your index.html file, there is something you can do to clean up our code... </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>To clean this template file, we can follow two approaches that are exactly the same, except that one is cached and the other one isn't. </p>
<ul>
<li>Computed Properties (cached)</li>
<li>Methods (not cached)</li>
</ul>
<p>In the next section, we will learn what computed properties are and how they differ from methods.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pBUXTUvDRCo" 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-computed-properties-and-methods">Computed Properties and methods</h2>
<p>You should use computed properties instead of in-template expressions for complex logic that has the scope of changing the presentation of our data, not the data itself. </p>
<p>If we need to change the data, then you should use methods instead. Computed properties are cached based on their dependencies, meaning that it will re-evaluate only when its dependencies have changed. </p>
<p>With computed properties, the result of the previously run function is returned if the dependencies have not changed. </p>
<p>You can view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/10-computed-properties/">repository here</a> and watch the video on <a target="_blank" href="https://youtu.be/VxFT6cgTHhw">YouTube here</a>. The video is also listed at the end of this section so you can review what you've learned.</p>
<p>In the following example, we will use computed properties but we could also use methods. Generally speaking, we use computed properties when we have an expensive operation that we want to execute and cache so that the next time we don't have to run it again unless something has changed.  </p>
<p>Let's implement a computed property for the following messages. Our HTML file will now change from this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>to this much cleaner version:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>We have replaced the contents of both spans with two new properties that will be placed as methods inside our computed object.</p>
<p>Now inside our Vue instance, we will create a new object called <code>computed</code> where we will define two methods that will return the messages we had before. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>
    },
    <span class="hljs-comment">// Computed Properties</span>
 <span class="hljs-attr">computed</span>: {
        <span class="hljs-attr">maxCharsText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.tweet.length}</span> of <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length}</span> characters`</span>;
        },
        <span class="hljs-attr">errorMessage</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max char limit reached! excess chars: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length - <span class="hljs-built_in">this</span>.tweet.length}</span>`</span>
        }
    },
    <span class="hljs-comment">// Methods</span>
 <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
                  <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                  <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
              } 
        }
    },

});
</code></pre>
<p>The first method <code>maxCharsText</code> returns exactly the same string we had before inside our HTML file. The only difference is that we are using the keyword <code>this</code> to reference the properties we needed to grab inside the Vue instance <code>this.tweet.length</code> and <code>this.max_length</code>. </p>
<p>The second method works in the exact same way and it also uses the keyword <code>this</code> to pick the properties defined in the Vue instance <code>this.max_length</code> and <code>this.tweet.length</code>.</p>
<h3 id="heading-all-together">All together</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Next<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"text in tweets"</span>&gt;</span>{{text}}<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>
</code></pre>
<p>JavaScript file</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>
    },
    <span class="hljs-comment">// Computed Properties</span>
 <span class="hljs-attr">computed</span>: {
        <span class="hljs-attr">maxCharsText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.tweet.length}</span> of <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length}</span> characters`</span>;
        },
        <span class="hljs-attr">errorMessage</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max char limit reached! excess chars: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length - <span class="hljs-built_in">this</span>.tweet.length}</span>`</span>
        }
    },
    <span class="hljs-comment">// Methods</span>
 <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
                  <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                  <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
              } 
        }
    },

});
</code></pre>
<p>If we want to use methods instead of computed properties, we can simply move both methods from the <code>computed</code> object inside the <code>methods</code> object and invoke them with parenthesis inside our HTML file like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText() }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage() }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Just remember that computed properties are cached while methods are not. 
If you want to learn more make sure to read the official <a target="_blank" href="https://vuejs.org/v2/guide/computed.html">documentation</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VxFT6cgTHhw" 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-how-to-create-a-simple-twitter-clone">How to Create a Simple Twitter Clone</h2>
<p>Now, let's put together everything we've learned so far and and build our very first project. It will be a minimalist and simplified twitter-like web application. </p>
<p>We want to create a simple application that has some kind of registration form, a box to add new tweets, and a section where we can show all tweets. We also want to be able to remove tweets.</p>
<p>All data must be persistent so that after the page is refreshed the list of tweets is still visible while the registration form will be hidden.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/v1j_bDDd6jI">YouTube here</a> or at the end of this section if you want to review.</p>
<p>You can also view the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/11-project-simple-twitter-cone/">here</a>.</p>
<h3 id="heading-define-our-tasks">Define our tasks</h3>
<p>Let's break this down into large tasks first. Then we will see what we need to do to complete each one. </p>
<ul>
<li>create a registration form</li>
<li>create a tweets box form</li>
<li>create a tweets section</li>
</ul>
<p>To do our project we need to research what tools we need to use to achieve our goals, so lets put them down:</p>
<ul>
<li>VueJS (application logic)</li>
<li>localStorage (make data persistent)</li>
<li>font awesome (icons)</li>
</ul>
<p>This application has no database so we are unable to record multiple users and their tweets. It's just a proof of concept, something we build to use our new knowledge.</p>
<h3 id="heading-create-the-project-structure">Create the project structure</h3>
<p>Now that we know what to do, let's start by creating our project structure and importing the tools we need to complete the first task, the registration form.</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">title</span>&gt;</span>Simple tweetter clone<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- CDN Fontawesome --&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">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span> /&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Style sheet --&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">"style.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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>

    <span class="hljs-comment">&lt;!-- Add a tweet --&gt;</span>

    <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Link our main.js file --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.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>Now that our HTML file is ready, let's create the main.js file and create a Vue instance.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {

    },
    <span class="hljs-attr">methods</span>: {

    }

});
</code></pre>
<p>Finally, we need to create a style.css file that we will place for now in the root folder of our project. </p>
<p>We will use a CSS file that I have already written, and you can download it from <a target="_blank" href="https://bitbucket.org/fbhood/simple-tweet-app/src/master/style.css">here</a>.</p>
<p>OK, our basic structure is ready to go. Inside our HTML file, we have some comments that reflect our 3 main tasks: create a registration form, create an add tweet box and, show a list of tweets. </p>
<p>Let's start with the first task and simulate a registration form.</p>
<h3 id="heading-how-to-simulate-a-registration-form-html">How to simulate a registration form - HTML</h3>
<p>Inside the root element <code>&lt;div id="app"&gt;&lt;/div&gt;</code> we need to create a registration form with the following fields: name, email, password, and a submit button. The form is contained in a card so we will wrap everything in a div and assign to it a card class. </p>
<p>The form won't submit data to a server, but will simply simulate a registration and update some property in the data object of the Vue instance.</p>
<p>We will place the following code inside our HTML file:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"16"</span> <span class="hljs-attr">required</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break this down. First, the form tag has a <code>v-on</code> directive with a <code>submit</code> argument so it will listen for a submit event. It also has an event modifier <code>.prevent</code> so when we hit the submit button the page doesn't refresh. </p>
<p>Inside the <code>v-on</code> directive there is a method called <code>registerAccount</code> that we need to create inside the methods of the Vue instance.</p>
<p>Inside the form, we have the three input fields: name, email, and password with labels. We wrapped each field inside a div with a class of <code>form_group</code>. Later we can replicate the style of the Twitter registration fields and show a character counter. </p>
<p>Each input field has a <code>v-model</code> directive that binds the input to its data property. </p>
<h3 id="heading-how-to-simulate-a-registration-form-vue">How to simulate a registration form - Vue</h3>
<p>Let's move on to the Vue instance to make the binding between the form and the data object properties.</p>
<p>Here we also need a place where we can store the details that the user submits so we will create another property for that. </p>
<p>Looking at the input fields, we also put a <code>maxlength</code> property on them, which is 25 for the name and email and 16 for the password. </p>
<p>Like we did previously when we learned about the v-model, we can create a property for these limits so that we can use it to show the user how many characters the user has left. </p>
<p>The Vue instance will look like this:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {}
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>
    },
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-comment">// record user details</span>
            <span class="hljs-comment">// add registration to localStorage</span>
            <span class="hljs-comment">// clear the registration fields</span>

        }
    }

});
</code></pre>
<p>Let's break this down. First inside the <code>data:{}</code> object, we defined an object to store and retrieve the <code>userData</code>. Then we added the properties <code>name</code>, <code>email</code>, and <code>password</code> to make the two-way data binding work. 
Finally we added the <code>max_length</code> and <code>max_pass_length</code> propeties.</p>
<h3 id="heading-how-to-show-input-character-counter">How to show input character counter</h3>
<p>OK, now that we have a binding between the input fields and our properties, we can show a character counter to the user while they're typing. </p>
<p>This is fairly simple – all there is to do is to show the length of each input property and compare it with the max length properties we have set in the Vue instance.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>So here we created a string using an in-template expression. We've shown the length of the <code>name</code> property and the value of the <code>max_length</code> so that while the user types we show something like this: 13/25. </p>
<p>We've also used a <code>v-bind</code> directive on the <code>maxlength</code> attribute so that its value is bound to the value of the property we defined in the Vue instance. So in case we want to change it we can do so in one place.</p>
<p>We will do the same for the other fields. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-add-the-logic-to-the-registeraccount-method">How to add the logic to the <code>registerAccount</code> method</h3>
<p>Now it's time to work on the form submission logic. We will simply populate the object stored in the property <code>userData</code> when the user submits the form. </p>
<p>Inside the <code>registerAccount</code> method we will add the details the user passes and build our object. </p>
<pre><code class="lang-js"> registerAccount(){
            <span class="hljs-comment">// record user details</span>
            <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
            <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
            <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            <span class="hljs-comment">// add registration to localStorage</span>

            <span class="hljs-comment">// clear the registration fields</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }
</code></pre>
<p>Here we have taken the value of the properties name, email, and password and assigned them to properties we created in the <code>userData</code> object.</p>
<p>This seems fine mostly because we have put on our input fields the <code>required</code> attribute – but if we remove it we will be able to submit an empty form, and we don't want that. </p>
<p>So let's add a very basic form of validation to at least check if the user has typed something inside our form, otherwise we show an error. </p>
<p>To do this, we need to add an if block inside the method and also an error property to the data object. Our file now looks like this:</p>
<pre><code class="lang-js">

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,

    },  
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) 
            {
                <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
            }

        <span class="hljs-comment">/* Add registration data to the local storage */</span>


        <span class="hljs-comment">/* Clear the registration inputs */</span>
        <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    }

});
</code></pre>
<p>Here in the <code>registerAccount</code> we've written a conditional that checks if the length of the name property is not empty, if the email property isn't empty, and if the password isn't empty  <code>this.name !== "" &amp;&amp; this.email !== "" &amp;&amp; this.password !== ""</code>. </p>
<p>If all these checks evaluate to a true value, then we run the code inside the block. Otherwise we run the code in the <code>else</code> block that updates the value of the <code>error</code> property that we will now use in our template to show the error message. </p>
<p>We also added a new property <code>usersID: 0,</code> that we used inside the if block to assign an id property to the <code>userData</code> object, just to make our application more realistic. But of course it is useless, as we will not have a database where we store all user details. We'll just store a single user inside their browser's local storage.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>


<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now our form is complete and we're also displaying an error message to the user if the required attributes are removed from our markup. </p>
<p>But our data do not persist and when the page is refreshed –everything's all gone. Let's tackle that using the localStorage API.</p>
<p>Inside our Vue instance, we need to set an item in the local storage. But we also need to save the entire <code>userData</code> object so that later we can use its data to display a message to the registered user.</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Add registration data to the local storage */</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
<span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))
</code></pre>
<p>Here we use the <code>setItem</code> method of the Local Storage API to add an item to the local storage so that later we can use it to check if the user is registered or not. </p>
<p>Then we also need to store the entire <code>userData</code> object as a string. To do that we use the <code>JSON.stringify</code> method that will turn the object into a JSON string that can be saved in the localStorage.</p>
<p>Our JS file is now as follows:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    },  
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name !== <span class="hljs-string">""</span>  &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {
                <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
            }

        <span class="hljs-comment">/* Add registration data to the local storage */</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
        <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))


        <span class="hljs-comment">/* Clear the registration inputs */</span>
        <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    }

});
</code></pre>
<p>Now when the user visits our application page, we need to check inside the browser's local storage to see if there is a key called <code>simple_tweet_registered</code>. If there is, we can assume that the user is registered and we can show the next section, the tweet box. Otherwise, we show the registration form. </p>
<p>We will do that by creating a <code>registered: false</code> property in the data object and use it to display or hide the registration form.</p>
<pre><code class="lang-js">
<span class="hljs-attr">data</span>: {
    <span class="hljs-attr">userData</span>: {},
    <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,      
}
</code></pre>
<p>Wrap the form around a div with a directive <code>v-if="!registered"</code> like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
    // here goes the form
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span> Tweetbox <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our final HTML file now looks like this:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">form</span>&gt;</span>


            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Add tweet --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome username_here write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">div</span>&gt;</span>
</code></pre>
<p>Now to make this work, we will use the lifecycle hook we created which lets us inject our code when the Vue instance has been created. This is because we want to check this before mounting our root element. </p>
<p>So let's add a life cycle hook to the Vue instance. We will check if our key is there, and if so we will update the value of the <code>registered</code> property to <code>true</code>. </p>
<p>We have also stored the full <code>userData</code> object in the local Storage so we can use it to repopulate the <code>userData</code> object when the page is refreshed with the details the user submitted.</p>
<pre><code class="lang-js">created(){
    <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
        <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
    }
    <span class="hljs-comment">// repopulate the userData object</span>
     <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

}
</code></pre>
<p>To turn a JSON string back into an object, we can use the <code>JSON.parse</code> method.</p>
<p>Now it's all set for the next task – show a tweet form to the user after registration.</p>
<p>Our code so far looks like this:</p>
<p>The main.js file:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }
        <span class="hljs-comment">// repopulate the userData object</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

    }

});
</code></pre>
<p>And the HTML inside the <code>app</code> element:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">form</span>&gt;</span>


        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Add tweet --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">div</span>&gt;</span>
</code></pre>
<p>Here in the HTML, since we used the <code>v-else</code> on the add tweet section and used the local storage to retrieve the data submitted by the user, we can use an in-template expression to grab the user name so that we can output a welcome message. </p>
<p>In the next section, we will create a tweet box form so that after the registration the user can write a tweet.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/v1j_bDDd6jI" 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-how-to-create-a-tweet-box-form-html">How to create a tweet box form - HTML</h3>
<p>Now it's time to build our add tweet form. We did something very similar earlier in this article, but now we will need to store and make the data persistent. This lets us show a list of tweets even when the page refreshes.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"sendTweet"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"tweet"</span>&gt;</span>
                Send your tweet
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ tweetMsg.length + '/' + max_tweet }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This is nothing new to us now. Inside the <code>tweetBox</code> element we add a form with the usual v-on directive and a method  <code>sendTweet</code> that we will need to define inside the methods object. This will take the tweet and save it somewhere, maybe in a property in the data object.</p>
<p>Inside the form, there is a <code>textarea</code> that has a <code>v-model</code> directive that binds it to a <code>tweetMsg</code> property that we need to create.</p>
<p>Finally, a submit button.</p>
<p>We also have a span inside the tweet label that shows a character counter to the user as we did before in the registration form. </p>
<p>Here we have a new property <code>max_tweet</code> that is used to show the limit and the <code>tweetMsg.length</code> is used to show the current number of the characters inserted.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/xFwfrIciFt0">YouTube here</a> if you want to review what you've learned.</p>
<h3 id="heading-create-a-tweets-box-form-vue">Create a tweets box form - Vue</h3>
<p>Let's go to the Vue instance and add the properties and the <code>sendTweet</code> methods.</p>
<p>Our data object now has three more properties, the <code>max_tweet</code> set to <code>200</code>, the <code>tweetMsg</code> that binds to the <code>textarea</code>, and a <code>tweets</code> array that we will use to store all tweets the user sends.</p>
<pre><code class="lang-js">data: {
    <span class="hljs-attr">userData</span>: {},
    <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">max_tweet</span>: <span class="hljs-number">200</span>, <span class="hljs-comment">// max tweets lenght</span>
    <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">tweetMsg</span>: <span class="hljs-string">""</span>, <span class="hljs-comment">// current tweet</span>
    <span class="hljs-attr">tweets</span>: [] <span class="hljs-comment">// list of tweets</span>
}
</code></pre>
<p>Inside the methods, we have a new method that will be invoked by the v-on directive when the form is submitted:</p>
<pre><code class="lang-js">
sendTweet(){
    <span class="hljs-comment">/* Store the tweet in the tweets property */</span>
    <span class="hljs-built_in">this</span>.tweets.unshift(
        {
            <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
            <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
        }

    );
    <span class="hljs-comment">/* Empty the tweetMsg property */</span>
    <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;
    <span class="hljs-comment">//console.log(this.tweets);</span>

    <span class="hljs-comment">/* Tranform the object into a string  */</span>
    stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
    <span class="hljs-comment">//console.log(stringTweets);</span>

    <span class="hljs-comment">/* Add to the local storage the stringified tweet object */</span>
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
},
</code></pre>
<p>The code above does four things:</p>
<ul>
<li>takes the tweets array and adds in it an object to represent a single tweet with text and date properties. To the text property, we assign the value of the <code>tweetMsg</code> that is bound with the <code>textarea</code>. For the date, we create a new date object with the <code>new Date().toLocaleTimeString()</code> method.</li>
<li>we empty the value of the <code>tweetMsg</code></li>
<li>we transform the tweets property into a string using the method <code>JSON.stringify(this.tweets)</code> </li>
<li>Then we add it to the local storage.</li>
</ul>
<p>Our final main.js file now looks like this:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        },
        sendTweet(){
            <span class="hljs-comment">/* Store the tweet in the tweets property */</span>
            <span class="hljs-built_in">this</span>.tweets.unshift(
                {
                    <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
                    <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
                }

            );
            <span class="hljs-comment">/* Empty the tweetMsg property */</span>
            <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;
            <span class="hljs-comment">//console.log(this.tweets);</span>

            <span class="hljs-comment">/* Tranform the object into a string  */</span>
            stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            <span class="hljs-comment">//console.log(stringTweets);</span>

            <span class="hljs-comment">/* Add to the local storage the stringified tweet object */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
        },


    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }
        <span class="hljs-comment">// repopulate the userData object</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

    }

});
</code></pre>
<p>Now that we've completed this part, we can show a list of tweets and also handle when the page is refreshed, and the local storage has in it our tweets object. We will need to parse it back and add its content to the <code>tweets</code> property to see the list.</p>
<p>Next, we will learn how to show the list of tweets using a v-for directive.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/xFwfrIciFt0" 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-how-to-show-a-list-of-tweets-html">How to show a list of tweets - HTML</h3>
<p>Inside our root element, add the following code:</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                    {{tweet.text}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetDate"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-calendar-alt fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>{{tweet.date}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</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">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we wrap everything in a div with a class <code>card_tweets</code>. Then we use a v-if directive inside a child section to check if there are tweets in the <code>tweets</code> array <code>v-if="tweets.length &gt; 0"</code>. </p>
<p>Inside this section, we can loop over the tweets array using a <code>v-for="(tweet, index) in tweets"</code> directive. After that we use in-template expressions to show the tweet text property <code>{{tweet.text}}</code> and the data <code>{{tweet.date}}</code>.</p>
<p>After the <code>section</code> we can use a <code>v-else</code> directive to show a message in case there are no tweets stored inside the tweets array <code>&lt;div v-else&gt;No tweets to show&lt;/div&gt;</code>. Done. </p>
<p>Now there's one last thing we need to do, and that is to figure out what to do to remove tweets from the list.</p>
<p>But when the user refreshes the page, everything is gone. So we need to work with the <code>localStorage</code> once again to repopulate our tweets array from it before rendering the root element.</p>
<p>Inside the <code>created</code> lifecycle hook we will now write some code to parse the tweets and save them back in the <code>tweets</code> property:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Parse all tweets from the local storage  */</span>
<span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_tweets"</span>)) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"There is a list of tweets"</span>);
    <span class="hljs-built_in">this</span>.tweets = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_tweets'</span>))
}<span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No tweets here"</span>);
}
</code></pre>
<p>Here we used the <code>localStorage</code> API to first check if there was a key called <code>simple_tweet_tweets</code>. If so, we grab the tweets property using <code>this.tweets</code> and assign to it the content of the <code>localStorage</code>. But we parse the string back to <code>JSON</code> with <code>JSON.parse</code> so we write <code>this.tweets = JSON.parse(localStorage.getItem('simple_tweet_tweets'))</code>.</p>
<p>Now everything works. After we refresh the page, the tweets are still there. Let's move on. In the next step, we will add a method to remove tweets from the list.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/3DzBkUHH3bU">YouTube here</a> or at the end of this section to review what you've learned.</p>
<h3 id="heading-how-to-remove-tweets">How to remove tweets</h3>
<p>Inside the div that contains the tweet message, we can add another div to show a link and a trash icon. This lets the user click it and remove that tweet. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-xs fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we simply used a v-on short syntax directive on the div and invoked a method <code>removeTweet(index)</code>, passing to the method the element index so that we know what to remove.</p>
<p>Let's build our <code>removeTweet</code> method now:</p>
<pre><code class="lang-js">removeTweet(index){
    <span class="hljs-keyword">let</span> removeIt = confirm(<span class="hljs-string">"Are you sure you want to remove this tweet?"</span>)
    <span class="hljs-keyword">if</span>(removeIt) {
        <span class="hljs-built_in">this</span>.tweets.splice(index, <span class="hljs-number">1</span>);
        <span class="hljs-comment">/* Remove the item also from the local storage */</span>
        <span class="hljs-built_in">localStorage</span>.simple_tweet_tweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
    }
}
</code></pre>
<p>This bit of code is pretty straightforward. Our method accepts an index that represents the position of the tweet object in the array obtained from the v-for directive when the method is invoked. </p>
<p>We then create a variable to ask the user to confirm that they want to delete the tweet. We used the <code>confirm</code> function for that. </p>
<p>If the value of the <code>removeIt</code> variable is true, then we execute the code and use <code>this.tweets.splice(index, 1)</code> to remove the tweet from the array using its index. </p>
<p>Finally we update the <code>localStorage</code> value by assigning to is the new array using the <code>localStorage.simple_tweet_tweets = JSON.stringify(this.tweets)</code>.</p>
<h3 id="heading-final-code">Final Code</h3>
<p>Our code is now complete. You can find the final code below or inside the repository here: [https://bitbucket.org/fbhood/simple-tweet-app/src/master/].</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">title</span>&gt;</span>Vue 2 Hello World<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">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-DZqqY3PiOvTP9HkjIWgjO6ouCbq+dxqWoJZ/Q+zPYNHmlnI2dQnbJ5bxAHpAMw+LXRm4D72EIRXzvcHQtE8/VQ=="</span>
        <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.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">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</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">"style.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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">form</span>&gt;</span>


            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Add tweet --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"sendTweet"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"tweet"</span>&gt;</span>
                        Send your tweet
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ tweetMsg.length + '/' + max_tweet }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                    {{tweet.text}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetDate"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-calendar-alt fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>{{tweet.date}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-xs fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</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">div</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<span class="hljs-tag">&lt;/<span class="hljs-name">div</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">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.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>JavaScript file</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">max_tweet</span>: <span class="hljs-number">200</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">tweetMsg</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">tweets</span>: []
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }, 
        sendTweet(){
            <span class="hljs-built_in">this</span>.tweets.unshift(
                {
                    <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
                    <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
                }

            );
            <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;

            <span class="hljs-comment">//console.log(this.tweets);</span>
            stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            <span class="hljs-comment">//console.log(stringTweets);</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
        },
        removeTweet(index){
            <span class="hljs-keyword">let</span> removeIt = confirm(<span class="hljs-string">"Are you sure you want to remove this tweet?"</span>)
            <span class="hljs-keyword">if</span>(removeIt) {
                <span class="hljs-built_in">this</span>.tweets.splice(index, <span class="hljs-number">1</span>);
                <span class="hljs-comment">/* Remove the item also from the local storage */</span>
                <span class="hljs-built_in">localStorage</span>.simple_tweet_tweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            }
        }
    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }

        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }
        <span class="hljs-comment">/* Parse all tweets from the local storage  */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_tweets"</span>)) {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"There is a list of tweets"</span>);
            <span class="hljs-built_in">this</span>.tweets = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_tweets'</span>))

        }<span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No tweets here"</span>);
        }
    }

});
</code></pre>
<p>We are ready to move forward with our Vue journey. Now it's time to learn about components.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/3DzBkUHH3bU" 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-vue-component-basics">Vue Component Basics</h2>
<p>A component is a reusable block of code that represents a specific element on the page.</p>
<p>Every web page and web or mobile application can be divided into components. Starting from the main sections we can further divide these into smaller bits and make sub-components. </p>
<p>Every component is reusable and is made of dedicated HTML, CSS, and JavaScript code. </p>
<p>We can use components to organize our code and build complex layouts that are easily maintainable.</p>
<p>Looking at a simple web page, it is usually made of a header, the main content area, and a footer. But each of these three pieces can be sub-divided into smaller parts. </p>
<p>For instance, a header can have the main navigation menu, a secondary menu, and a hero image. The same is true for the main and footer areas.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/wrqjPka7puo">YouTube here</a> or at the end of this section to review. You can also view the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/12-components-basics/">here</a>.</p>
<p>To get started with components, we first need to learn how to register them, pass them data, and then we need to learn how to use them. Here are some great overviews of these topics to get you started:</p>
<ul>
<li>Register a component (https://vuejs.org/v2/guide/components-registration.html)</li>
<li>How to use Props (https://vuejs.org/v2/guide/components-props.html)</li>
<li>How to use Slots (https://vuejs.org/v2/guide/components-slots.html)</li>
<li>How the Data object works inside a component   </li>
<li>Child Component Events (https://vuejs.org/v2/guide/components-custom-events.html)</li>
<li>Dynamic Components ( https://vuejs.org/v2/guide/components-dynamic-async.html)</li>
</ul>
<h3 id="heading-how-to-register-a-component-in-vue">How to register a component in Vue</h3>
<p>To register a component, we need to use the <code>component</code> method on the <code>Vue()</code> object. After calling this function we need to define a template property with some markup specific to the component. </p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'component-name'</span>, {
    <span class="hljs-comment">// component properies here</span>
});
</code></pre>
<p>Every component needs to have a template property at least – without it a component doesn't make much sense.</p>
<p>So the next step is to define a template property and pass to it a string literal with some HTML tag:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'test-component'</span>, {
    <span class="hljs-comment">// component properies here</span>
    <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;p&gt;I am a component&lt;/p&gt;`</span>

});
</code></pre>
<p>Now we can use our component multiple times inside our main HTML file by using its name as it was a standard HTML tag.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">test-component</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">test-component</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>However, our component will always render the same content, <code>I am a component</code>. Let's make it more useful and, following our tweets example, build a tweet message component.</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>, {

    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; Tweet text goes here &lt;/p&gt;
           &lt;p&gt; Date of the tweet goes here&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>OK, now that we have the base for our component we need to actually pass data to it.</p>
<p>One thing to notice here is that every component requires a single root element inside the template property. So, since we have two paragraphs, we wrapped them inside a div that will be considered the root element of our component. </p>
<p>Inside it, we can put whatever we want to build our custom component.</p>
<p>Let's move on to the next step and pass some data to the component.</p>
<h3 id="heading-how-to-use-props-in-vue">How to use props in Vue</h3>
<p>Now, given what we've learned so far, we want to pass data to our component as we did previously to bind data between the Vue instance and the markup file via the moustache syntax. </p>
<p>However, with components things work a bit differently. We use props to create a binding between our component and its template.</p>
<p>The <code>props</code> property can be defined as an array or as an object. 
When used as an array, we can specify the properties as strings inside the array and these can later be used inside the component like we usually do. </p>
<p>When we use an object we can pass the prop as the key and its type as the value. That will help to make sure that the exact data type is passed to our component. </p>
<p>Let's see an example of that.</p>
<p>Example of props as an Array:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: [<span class="hljs-string">'text'</span>, <span class="hljs-string">'date'</span>]
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Use props as an object where the key is the property and the value is its type:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Once we have defined our properties, we can use them as HTML attributes and pass them the data we want our component to render onto the page. </p>
<p>For instance, we can use the component above to show a bunch of tweets using our newly created component.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- Manually pass the data to the tweet message component --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is a component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"25/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"26/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"27/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Pass a javascript expression to the date property of the tweet message component --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">:date</span>=<span class="hljs-string">"new Date().toLocaleString()"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
</code></pre>
<p>The first examples will render the string we passed between quotes. But 
to render the computed result of the new <code>Date()</code> instance we will need to use the v-bind directive so that its content is interpreted as JavaScript code.</p>
<p>You can review all this in the docs here: [https://vuejs.org/v2/guide/components-props.html].</p>
<h3 id="heading-the-data-property-inside-components">The data property inside components</h3>
<p>So far we have seen that we can bind data by defining properties inside the <code>data</code> object on a Vue instance. </p>
<p>When working with components the data object is not available as an object but as a function. This function can return an object with properties. This will make each component's instance unique and independent from the others.</p>
<p>Following our previous example, let's add a couple of CSS classes to our component.</p>
<p>First, we will edit our component template and bind the class attribute to a data property. Then we will create our data object.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Now our template will look for two data properties, <code>tweetBoxWrapper</code> and <code>dateClass</code>, that we can later use inside our CSS to add some style to our elements. Let's add the data function now.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-comment">// Data properties go here</span>
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
        }
    }
});
</code></pre>
<p>Another thing we can do is to define a data property and use it inside our template, for instance, to dynamically show the current date. We can define a <code>now</code> property and use it in the template like we previously did with the <code>date</code> property:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'text'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt;{{now}}&lt;/p&gt;

       &lt;/div&gt;

    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
            <span class="hljs-attr">now</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString(), <span class="hljs-comment">// 3 </span>

        }
    }


});
</code></pre>
<p>In the example above we have used both <code>props</code> and <code>data</code>. We can use the prop <code>text</code> as an attribute when we use our component <code>&lt;tweet-message text="This is a component"&gt;&lt;/tweet-message&gt;</code>. The properties we returned in the <code>data</code> method are bound to the template and will render the information we specify right there in the data method.</p>
<p>When inside the data method, we need to remember that props defined here are accessible using the <code>this</code> keyword.</p>
<p>So if we want to store the value of the text prop inside a property in the data object, we can grab it like this:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'text'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt;{{now}}&lt;/p&gt;

       &lt;/div&gt;

    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
            <span class="hljs-attr">now</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString(), 
            <span class="hljs-attr">message</span>: <span class="hljs-built_in">this</span>.text
        }
    }


});
</code></pre>
<p>Next, we will learn about slots.</p>
<h3 id="heading-how-to-use-slots">How to use slots</h3>
<p>There are situations when we just don't know or want to strictly define what goes inside a component. Or we might want to let the user decide its content when they use our component.</p>
<p>In such cases, we can use slots when we declare the template of our component.</p>
<p>Let's imagine that we have another component that we want to use to divide tweets into different sections.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-section'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'title'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
        &lt;div class="tweet_section"&gt;
            &lt;h2&gt;{{title}}&lt;/h2&gt;
           &lt;slot&gt;&lt;/slot&gt;
       &lt;/div&gt;  
    `</span>    
});
</code></pre>
<p>Our new component can be as simple as that, a div with a class <code>tweet_section</code>, an <code>h2</code> that binds to a prop, and a slot. The slot means that inside our component we can put whatever we want, like nesting other elements and even other components.</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">tweet-section</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Latest Tweets"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my first tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my second tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my third tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my fourth tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">tweet-section</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">tweet-section</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Most popular"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Trendy in IT<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is a very popular tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is another popular tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tweet-section</span>&gt;</span>
</code></pre>
<p>We've barely scratched the surface here, but with what we know we can already modify our <code>simple_twitter</code> application to use components. Along the way, we will also learn how events work inside components.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/wrqjPka7puo" 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-how-to-update-your-simpletwitter-project-with-components">How to Update Your Simple_twitter Project with Components</h2>
<p>Now that we have a basic understanding of components, we can update the simple Twitter project we built in the previous videos and use components to make our code better.</p>
<p>We need to do a few things to make this happen, and create a component:</p>
<ol>
<li>We need to decide what component we want to build </li>
<li>We need to extract the code from the markup and place it in the template property</li>
<li>We need to refactor our code to make the component work.</li>
</ol>
<p>You can watch the tutorial on <a target="_blank" href="https://youtu.be/HanHyGFC6Sc">YouTube here</a>
or checkout the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/13-simple-twitter-components/">here</a>.</p>
<p>Let's say we want to create a component for the tweet message. </p>
<h3 id="heading-how-to-create-the-component">How to create the component</h3>
<p>Let's create a component for a tweet message like this:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">template</span>: <span class="hljs-string">``</span>
});
</code></pre>
<h3 id="heading-how-to-move-the-tweetmsg-element">How to move the tweetMsg element</h3>
<p>Then we have to move the <code>tweetMsg</code> element inside the  <code>template</code> property of our component:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg" v-for="(tweet, index) in tweets"&gt;
        &lt;p&gt;
            {{ tweet.text}}
        &lt;/p&gt;
        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar fa-sm fa-fw"&gt;&lt;/i&gt;{{ tweet.date }}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="removeTweet(index)"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-sm fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    `</span>
});
</code></pre>
<p>After that, we need to update the template because the v-for directive now is useless. So we will remove it and add it back later when we are ready to use the component.</p>
<p>Given that we will not have a v-for directive at this point, we still want to use the tweet variable to grab the tweet, so we will pass it as a <code>props</code>.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'tweet'</span>: <span class="hljs-built_in">Object</span>,
    },
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg"&gt;
        &lt;p&gt;
            {{ tweet.text}}
        &lt;/p&gt;
        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar fa-sm fa-fw"&gt;&lt;/i&gt;{{ tweet.date }}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="removeTweet(index)"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-sm fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    `</span>
});
</code></pre>
<h3 id="heading-how-to-emit-a-custom-event">How to emit a custom event</h3>
<p>There is also an event listener that needs to change to let our application work as expected. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The code here <code>&lt;div class="tweet_remove" @click="removeTweet(index)"&gt;</code> listens to click events so the user can remove a tweet by clicking on it. </p>
<p>This will need to go, and we need to replace it with a special method of the Vue instance called <code>$emit()</code>. Our component instance will need to communicate with the parent instance and tell it that it wants to trigger the remove tweet method.  </p>
<p>To solve this problem, Vue provides a custom events system. It allows us to use the v-on directive to listen not only to native DOM events but also to custom events defined at the component level.</p>
<p>We need to update this line of code: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
</code></pre>
<p>and change it like so: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"$emit('remove-tweet', 'index')"</span>&gt;</span>
</code></pre>
<p>Let's break this down: we keep using the v-on directive in its short form <code>@</code>. Then we use the Vue <code>$emit</code> method to define a custom event that our component will emit when we click on this element. </p>
<p>To the <code>$emit</code> method we pass two parameters, the first is the name of the custom event <code>remove-tweet</code>, and the second is a parameter that we want to pass to the event listener when we use <code>index</code>. That will be the index of the element we want to delete. </p>
<p>So that the parent instance can listen to our event, trigger the <code>removeTweet</code> method we defined in the main Vue instance and remove the correct tweet.</p>
<h3 id="heading-put-it-all-together-1">Put it all together</h3>
<p>Our final component now looks like this:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'tweet'</span>: <span class="hljs-built_in">Object</span>,
    },
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg"&gt;
        &lt;p&gt;
            {{tweet.text}}
        &lt;/p&gt;

        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar-alt fa-sm fa-fw"&gt;&lt;/i&gt;{{tweet.date}}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="$emit('remove-tweet', 'index')"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-xs fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;

    &lt;/div&gt;
    `</span>
});
</code></pre>
<p>And we'll change our index.html file as follows:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>  <span class="hljs-attr">v-bind:tweet</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"index"</span> @<span class="hljs-attr">remove-tweet</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that we've completed our first project, let's learn how to make an API request and how to use the GitHub API to build our final portfolio.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/HanHyGFC6Sc" 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-how-to-perform-api-calls-with-axios">How to Perform API Calls with Axios</h2>
<p>For our next project, I have created a simple but nice design using Figma that we will use to kick start our portfolio.</p>
<p>Our portfolio will use the GitHub's rest API to pull projects and fill out the design. </p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/XJEmPr89HA8">YouTube here</a>
and check out the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/14-axios/">BitBucket</a>.</p>
<h3 id="heading-what-is-a-rest-api">What is a REST API?</h3>
<p>To quote Wikipedia:</p>
<blockquote>
<p>"A REST API is a software architectural style that enables the requesting system to access and manipulate a textual representation of web resources."</p>
</blockquote>
<p>What this means is that our Vue application (the requesting system) will request a textual representation from GitHub of our repositories that we can use later and manipulate to showcase our projects inside our portfolio.</p>
<p>For our final project, we will use a library called Axios that will help us make HTTP requests to the GitHub API. </p>
<p>We can install Axios inside our project in multiple ways. For our example we will keep things simple and use the CDN. </p>
<p>There are also other methods you can use to install Axios. The official documentation for Axios is available <a target="_blank" href="https://www.npmjs.com/package/axios">here</a>
and you can read about how to consume the API in the <a target="_blank" href="https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Base-Example">documentation here</a>.</p>
<h3 id="heading-how-to-install-axios-via-cdn">How to install Axios via CDN</h3>
<p>So let's get started and install Axios via the CDN. We will use the UNPKG CDN and insert a script tag inside our main HTML file. </p>
<p>This CDN will always provide the most up to date version of Axios. Alternatively, we can also specify a different version number. </p>
<p>Let's start by inserting the following script in an index.html file that we will use to send our first HTTP request to the GitHub API.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Our final HTML file will look like this now:</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">title</span>&gt;</span>VueJS / GitHub API<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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Axios latest version CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.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">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.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>The code above is nothing new, but let's look at it piece by piece.</p>
<p>We have a basic HTML structure. Before the closing body tag, we've placed two script tags, one for Axios and one for VueJs. </p>
<p>In the body, we created a root element for the Vue application that we called <code>#app</code>.</p>
<p>Finally, before the end of the body tag, we placed a new script tag that points to the file where we will write our code, the main.js file.</p>
<p>Now we have all the building blocks to make our first API call and request data from the GitHub API.</p>
<p>But before that let's quickly see what an HTTP request actually is and what kind of requests we can make.</p>
<h3 id="heading-what-is-an-http-request">What is an HTTP request?</h3>
<p>HTTP stands for Hypertext transfer protocol. It is an application-layer protocol designed for communications between two points:</p>
<ol>
<li>a web client (the browser) </li>
<li>a web server</li>
</ol>
<p>This protocol allows transmission of data like HTML files and. It defines verbs also known as methods that you can use to perform specific actions on a given resource.</p>
<p>The method or verb that we will use for our project is the <code>GET</code> method, that, as you might have guessed, is used to obtain or to get 
a resource from the webserver.</p>
<p>We have also other methods:</p>
<ul>
<li>GET (retrieves data)</li>
<li>POST (sends data)</li>
<li>PUT (updates the entire representation of the data)</li>
<li>PATCH (similar to put but used to partially update data)</li>
<li>DELETE (removes data)</li>
</ul>
<p>Each of these requests performs a specific action on a resource, but there are also other verbs like the HEAD, OPTIONS, CONNECT, and TRACE.</p>
<p>I won't cover HTTP in detail here as it's out of the scope of this guide. But below there are some links to documentation pages 
related to this topic if you want to find out more. </p>
<p>I suggest you to read the following at least:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-the-internet-works/">HTTP Intro</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP Methods</a></li>
</ul>
<h3 id="heading-how-to-perform-a-get-request">How to perform a GET request</h3>
<p>We will use the GET method to perform get requests from the GitHub API. All data we want to request are publicly accessible (the public repositories of a user), therefore we don't have to authenticate our application. </p>
<p>But unauthenticated requests are limited. For the scope of this tutorial, this is perfectly fine. If you plan to put this in production then you might want to look at how to make authenticated requests and obtain an API key from GitHub.</p>
<p>GitHub provides clear and in-depth documentation about its Rest API, including a list of resources that you can request along with their endpoints. We will use the "List repositories for a user" resources available <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#list-repositories-for-a-user">here</a>.</p>
<p>Let's look at the documentation. The first thing we notice is that GitHub gives us a GET endpoint where we can send our HTTP requests <code>/users/{username}/repos</code>. </p>
<p>The placeholder <code>{username}</code> needs to be replaced with the actual username of the user we want to request the list of public repositories from.</p>
<p>From the documentation, we also see that there are other parameters that we can use to refine our request. We will use <code>username</code> that goes in the path and needs to be a string as described in the parameters table under Type. </p>
<p>We can also use the <code>per_page</code> and <code>page</code> parameters to paginate our results.</p>
<p>Let's make the first request and see what we get.</p>
<p>Inside our main.js file, we will create a new Vue instance and add a <code>mounted</code> lifecycle hook where we will perform the HTTP request using Axios.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>:{
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>
    },
    mounted(){

         axios
         .get(<span class="hljs-string">`https://api.github.com/users/fabiopacifici/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
         .then(
            <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                <span class="hljs-built_in">console</span>.log(response);
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
        )
        .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>Let's break this code down. First, we have created a new Vue instance. Then we used the <code>el</code> property and assigned it a root HTML element. </p>
<p>Then we have defined a <code>data</code> object and the properties that we will use later to perform the HTTP request and handle the response.</p>
<p>After the data object, we have defined a lifecycle hook that will use to run our code once the root element has been mounted.</p>
<p>Inside the <code>mounted</code> method, it's time to use Axios and perform an HTTP request. </p>
<p>Axios is a promise-based HTTP client. When we use the get method to request our data from the GitHub API it will return a promise that needs to be handled. </p>
<p>We do this using the syntax <code>axios.get()</code> to perform the request, then we handle its response using the <code>.then()</code> method on the promise. </p>
<p>If our request fails the <code>.catch()</code> method will handle the error and, in this case, show the error message on the console.</p>
<p>Promises are out of the scope of this guide, but if you want to learn more, you can check out <a target="_blank" href="https://www.freecodecamp.org/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/">this detailed article here</a>.</p>
<p>Inside the <code>.get()</code> method we have put the URL including a query string that uses <code>per_page</code> and <code>page</code> parameters to submit our request. Inside the <code>.then()</code> method we handled the response. The response parameter is given to us by the promise and we use an arrow function to handle it.</p>
<pre><code class="lang-js">response =&gt; {
                <span class="hljs-built_in">console</span>.log(response);
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
</code></pre>
<p>The get method returns a promise. Here we simply handled its <code>response</code> with an arrow function where <code>response</code> is the return value that we obtained by calling <code>axios.get()</code>.</p>
<p>We logged the response object to the console. Then we assigned its content, the <code>response.data</code>, to our <code>projects</code> property so that we can later retrieve each project and show them onto the page as usual with a <code>v-for</code> directive.</p>
<h3 id="heading-how-to-show-each-project">How to show each project</h3>
<p>Now it's time to show our projects inside the portfolio. We can do that with the v-for directive.</p>
<p>The projects property in this case contains an array of objects. Each object has its properties that we can use to populate our template.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'app'</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{project.full_name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"author"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"me"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span>&gt;</span>View<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we use the v-for directive to loop over the array of projects.
Now the <code>project</code> variable contains an object that represents a single repository from the GitHub account.</p>
<p>Looking at the response object we know that we can grab a number of properties. So we picked <code>full_name</code>, the full name for the repository, <code>owner.avatar_url</code>, the URL of the profile's avatar, and <code>html_url</code> that is the actual URL of our repository. That's all we need for now. </p>
<p>If we now look at the page we will immediately see all repositories from our account.</p>
<p>Now that we know how to make an HTTP request with Axios and get data from GitHub, we are almost ready to start building our portfolio. </p>
<p>In the next section, we are going to look at another Vue library called Vue-router that we will use in our final project.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/XJEmPr89HA8" 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-how-to-handle-routing-with-vuerouter">How to Handle Routing with VueRouter</h2>
<p>Our portfolio will surely have more than one page, so we need a system that understands where to send the user when, for instance, they click a link in the navbar for a specific page. </p>
<p>For that Vue has an official routing package that can help us do just that and build a single page application.</p>
<p>A single page application is an application that doesn't refresh the page when a user visits a new page so that the user experience is more fluid.</p>
<p>As for Vue and Axios, we need to install this library and we do that via its CDN. But as always, there are also other methods depending on your needs. I just want to keep things simple for now, so let's start by placing the CDN script tag inside the HTML file and learn the basics of this new library.</p>
<p>You can watch the tutorial on <a target="_blank" href="https://youtu.be/T_avTRFAEAg">YouTube here</a>
and checkout the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/15-routing/">BitBucket</a>.</p>
<p>You can also see the Vue Router Documentation <a target="_blank" href="https://router.vuejs.org/installation.html#direct-download-cdn">here</a>.</p>
<h3 id="heading-how-to-install-vue-router-via-cdn">How to install Vue Router via CDN</h3>
<p>Let's take our previous example index.html and after the VueJS CDN will point to the router <code>https://unpkg.com/vue-router@3.4.9/dist/vue-router.js</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">title</span>&gt;</span>VueJS / GitHub API<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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span> 
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{project.full_name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"author"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"me"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span>&gt;</span>View<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-comment">&lt;!-- Axios latest version CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Main scrip file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.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>
<h3 id="heading-how-to-use-vue-router">How to use Vue Router</h3>
<p>Now our app has access to the router system and we can add a couple of routes for our application.</p>
<p>We can do so using the <code>router-link</code> component provided by the library and its <code>to</code> attribute to point the link to a specific page.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Create a router link using the 'router-link' component and set the path using the 'to' attribute --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
</code></pre>
<p>We also used the router-view component that will render a specific component for each route.</p>
<p>Now we need to do something inside our JavaScript file to make this work. </p>
<p>Let's see the steps we need to take:</p>
<ul>
<li>Define route components</li>
<li>Define routes</li>
<li>Create a Vue router instance</li>
<li>Create and mount the Vue root instance.</li>
</ul>
<p>First, we need to define our components that we'll use from each route to render the content of the page.</p>
<p>We will create two components, one for the home page and one for the projects page.</p>
<p>To simplify the steps, we will keep everything in the same file and refactor later on. </p>
<p>Let's create the first two basic components to see if the router works:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create Route components</span>
<span class="hljs-keyword">const</span> Home = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt;My Portfolio&lt;/div&gt;'</span>} 
<span class="hljs-keyword">const</span> Projects = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt; Projects &lt;/div&gt;'</span>}
</code></pre>
<p>Now let's follow the remaining steps and define the routes, create the vue router instance, and create and mount the Vue root instance.</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Define some routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects}
];
<span class="hljs-comment">// Create the router instance and pass the routes to it</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
<span class="hljs-attr">routes</span>: routes
});
<span class="hljs-comment">// Create and mount the root instance.</span>

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    router 
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>That's it. If you visit the homepage you will see two navigation links and the site content will change accordingly.</p>
<p>Let's put it all together and start building our portfolio.</p>
<p>From the previous example in the Axios section, we requested from the GitHub API all public repositories for a user and rendered name, user avatar, and project URL onto the page. </p>
<p>Let's move some of that logic inside our application that uses routes.</p>
<p>The main changes that we need to make here are:</p>
<ul>
<li>move the HTML markup inside the <code>template</code> property of the project's component</li>
<li>move the <code>data</code> properties inside the <code>data</code> object of the component</li>
<li>move the code we wrote in the mounted hook inside our component.</li>
</ul>
<p>The final code looks something like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Define route components</span>

<span class="hljs-keyword">const</span> Home = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt;My Portfolio&lt;/div&gt;'</span>} 
<span class="hljs-keyword">const</span> Projects = {

    <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt; 
         &lt;div v-for="project in projects"&gt;
            &lt;h2 class="title"&gt;{{project.full_name}}&lt;/h2&gt;

            &lt;div class="author"&gt;
                &lt;img width="50px" :src="project.owner.avatar_url" alt="me"&gt;
            &lt;/div&gt;
            &lt;div class="view"&gt;
                &lt;a :href="project.html_url"&gt;View&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;`</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">projects</span>: [],
            <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
            <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>
        }
    }, 
    mounted(){

         axios
         .get(<span class="hljs-string">`https://api.github.com/users/fabiopacifici/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
         .then(
            <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                <span class="hljs-comment">//console.log(response);</span>
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
        )
        .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-comment">// Define some routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects}
];
<span class="hljs-comment">// Create the router instance and pass the routes to it</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
<span class="hljs-attr">routes</span>: routes
});
<span class="hljs-comment">// Create and mount the root instance.</span>

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    router 
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>The HTML file remains the same:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'app'</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">nav</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">nav</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">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that we have a base to work with, let's improve it. We will use a design prototype I made using Figma and add some functionalities to our portfolio to make it look nice.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/T_avTRFAEAg" 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-final-project-how-to-build-a-portfolio-with-vuejs-vuerouter-axios-github-api-and-deploy-to-netlify">Final Project – How to Build a Portfolio with VueJS, VueRouter, Axios, GitHub API and deploy to Netlify</h2>
<p>We are ready to build our final project! For our Vue-folio, we will start from where we left off in the previous section.</p>
<p>We will build a single-page application that has two routes, one for the home page and one for the projects page. </p>
<p>Below are the building blocks:</p>
<ul>
<li>Vuejs </li>
<li>Vue router</li>
<li>Axios</li>
<li>GitHub rest API</li>
<li>portfolio design</li>
</ul>
<p>You can watch this tutorial on <a target="_blank" href="https://youtu.be/I6hQnWQU4rQ">YouTube here</a> and check out the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/16-final-project-portfolio/">BitBucket here</a>.</p>
<h3 id="heading-project-structure">Project structure</h3>
<p>To speed things up we will just copy the code we wrote in the previous section.</p>
<p>The project structure will be the following:</p>
<pre><code>|-- index.html
|-- assets/
    |-- css/
        |-- style.css
    |-- js/
        |-- main.js
    |-- img/
</code></pre><h3 id="heading-indexhtml-file">index.html file</h3>
<p>The index.html file is a little different from what we had in the previous section. Here, we will place only the router-view component that is responsible for showing the component matching a given route. </p>
<p>Then we will place the actual <code>route-links</code> inside each component to make sure we have the desired result as per the design.</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">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-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">title</span>&gt;</span>Vuefolio<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">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Raleway:wght@100;300;400;900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</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">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</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">"./assets/css/style.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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</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">footer</span>&gt;</span> © Developed by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fabiopacifici.com"</span>&gt;</span>Fabio Pacific<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Main Js file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/js/main.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>
<h3 id="heading-stylecss-file">style.css file</h3>
<p>Since this is not going to be a CSS tutorial, for the CSS part you can simply copy the code from the repository file if you are following along.</p>
<pre><code class="lang-css">
    <span class="hljs-comment">/* Utility Classes */</span>
    <span class="hljs-selector-class">.d_none</span> {
        <span class="hljs-attribute">display</span>: none;
    }
    <span class="hljs-selector-class">.d_flex</span> {
        <span class="hljs-attribute">display</span>: flex;
    }
    <span class="hljs-selector-class">.container</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1170px</span>;
        <span class="hljs-attribute">margin</span>: auto;
    }
    <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: white;
    } 
    <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>:<span class="hljs-number">#DB5461</span>;

    }
    <span class="hljs-selector-class">.loading</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
    }
    <span class="hljs-comment">/* END Utility Classes */</span>
    <span class="hljs-comment">/* Components */</span>
    <span class="hljs-selector-class">.bio__media</span> {
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: flex-start;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">text-align</span>: left;
    }
    <span class="hljs-selector-class">.bio__media</span> <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;
    }
    <span class="hljs-selector-class">.bio__media__text</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    }
    <span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">h1</span>{
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;

    }
    <span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">p</span> {
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;

    }

    <span class="hljs-selector-class">.card__custom</span> {
        <span class="hljs-attribute">position</span>: relative;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
        <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> /<span class="hljs-number">2</span>);
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">justify-content</span>: space-between;
    }
    <span class="hljs-selector-class">.card__custom</span> &gt; <span class="hljs-selector-class">.card__custom__text</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100%</span> / <span class="hljs-number">3</span>) *<span class="hljs-number">2</span>);
        <span class="hljs-attribute">text-align</span>: right;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">justify-content</span>: space-around;
        <span class="hljs-attribute">overflow</span>: hidden;

    }
    <span class="hljs-selector-class">.card__custom__img</span> {

        <span class="hljs-attribute">position</span>: absolute;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/cards_bg_img.svg);
        <span class="hljs-attribute">background-position</span>: center;
        <span class="hljs-attribute">background-repeat</span>: no-repeat;
        <span class="hljs-attribute">background-size</span>: contain;
        <span class="hljs-attribute">display</span>: inline-block;
        <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
        <span class="hljs-attribute">left</span>: <span class="hljs-number">60%</span>;
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">85px</span> <span class="hljs-number">0</span> <span class="hljs-number">100px</span> <span class="hljs-number">25px</span>;

    }
    <span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span>, <span class="hljs-selector-class">.btn_load_more</span> {
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#F1EDEE</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
        <span class="hljs-attribute">box-sizing</span>: border-box;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">54px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#3D5467</span>;
    }
    <span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.btn_load_more</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">cursor</span>: pointer;
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#324555</span>;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: <span class="hljs-number">1s</span>;
    }
    <span class="hljs-selector-class">.card__custom__text</span> <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
    }
    <span class="hljs-comment">/* END Componenet */</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">box-sizing</span>: border-box;
    }

    <span class="hljs-selector-tag">body</span>{
        <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, Arial, Helvetica, sans-serif;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(<span class="hljs-number">116.82deg</span>, #<span class="hljs-number">3</span>D5467 <span class="hljs-number">0%</span>, #<span class="hljs-number">1</span>A232B <span class="hljs-number">99.99%</span>, #<span class="hljs-number">333333</span> <span class="hljs-number">100%</span>);

    }
    <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">text-decoration</span>: none;   
    }

    <span class="hljs-comment">/* Home Page */</span>
    <span class="hljs-selector-tag">main</span><span class="hljs-selector-id">#home</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">600px</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: center;
        <span class="hljs-attribute">align-items</span>: center;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">28px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;

    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span>  {
        <span class="hljs-attribute">font-style</span>: normal;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">42px</span>;
        <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">0.115em</span>;

    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> <span class="hljs-selector-tag">p</span> {
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">22px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
    }
    <span class="hljs-selector-class">.skills_projects_link</span> {
        <span class="hljs-attribute">position</span>: relative;
    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">21px</span>;

    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease-in-out;

    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">::after</span> {
        <span class="hljs-attribute">position</span>: absolute;
        <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">margin</span>: auto;
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.5s</span> ease-in-out;

    }

    <span class="hljs-comment">/* Header */</span>
    <span class="hljs-selector-id">#site_header</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
        <span class="hljs-attribute">justify-content</span>: space-between;
        <span class="hljs-attribute">align-items</span>: center;
    }
    <span class="hljs-selector-id">#site_header</span> &gt; <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
    }
    <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#e2e2e2</span>;
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    }
    <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    }
    <span class="hljs-comment">/* Portfolio Page Section */</span>

    <span class="hljs-selector-id">#portfolio</span> {
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-wrap</span>: wrap;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">justify-content</span>: space-around;
    }
    <span class="hljs-selector-class">.btn_load_more</span> {

    }
    <span class="hljs-comment">/* Skills */</span>

    <span class="hljs-selector-id">#skills_section</span> {
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/skills_bg.svg);
        <span class="hljs-attribute">background-repeat</span>: no-repeat;
        <span class="hljs-attribute">background-size</span>: contain;
        <span class="hljs-attribute">background-position</span>: top left;
    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">h2</span> {
        <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">180px</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">44px</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#F1EDEE</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2rem</span>;

    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span> {
        <span class="hljs-attribute">list-style</span>: none;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">120px</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-wrap</span>: wrap;

    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span>  <span class="hljs-selector-tag">li</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">35px</span>;
    }



    <span class="hljs-selector-class">.avatar</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
        <span class="hljs-attribute">height</span>: auto;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;

    }




    <span class="hljs-selector-class">.card__back</span> {
        <span class="hljs-attribute">display</span>: none;
    }
    <span class="hljs-selector-class">.rotate__card</span> {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate3d</span>(<span class="hljs-number">360</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">180deg</span>);
    }
    <span class="hljs-comment">/* Site Footer */</span>

    <span class="hljs-selector-tag">footer</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    }



    <span class="hljs-comment">/* Media Query  */</span>

    <span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">475px</span>) {
        <span class="hljs-selector-class">.card</span> {
            <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }
    }
</code></pre>
<h2 id="heading-mainjs-file-basic-structure">Main.js file basic structure</h2>
<p>Inside the main.js file, we have the core of our single page application. 
Here we will define the route components that need to be rendered for each view/page, the homepage, and projects components. </p>
<p>Then we will define two routes, one for the homepage and one for the projects page, create a router instance, and pass it to the routes. Finally, we will create a new Vue instance and pass to it the router instance and mount the root HTML element.</p>
<p>Let's start with the route components.</p>
<h3 id="heading-how-to-define-components-for-each-view">How to define components for each view</h3>
<p>The homepage component is fairly simple. </p>
<pre><code class="lang-js"><span class="hljs-comment">// Homepage component</span>
<span class="hljs-keyword">const</span> Home = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;main id="home"&gt;
        &lt;div class="about__me"&gt;
            &lt;img src="./assets/img/avatar.svg" alt=""&gt;
            &lt;h1&gt;John Doe&lt;/h1&gt;
            &lt;h3&gt;Python Expert&lt;/h3&gt;
            &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;

            &lt;div class="skills_projects_link"&gt;
                &lt;router-link to="/projects"&gt;Projects/Skills&lt;/router-link&gt; 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;`</span>
}
</code></pre>
<p>Let's break it down. First, we create a Home constant that will hold the router component object. </p>
<p>Inside the object, the only thing we will put is the template property with some markup to render our page. The main thing to notice here is the router-link component that will point to the /projects route from the homepage.</p>
<p>Next, let's create a route component for the projects page. We have a lot to do here so for now let's just add some boilerplate code – we will come back to it later and write out step by step all the logic. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Projects = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;div&gt;
        &lt;h1&gt;Projects&lt;/h1&gt;
    &lt;/div&gt;`</span>,
    data() { 
        <span class="hljs-keyword">return</span> {
                <span class="hljs-comment">// Data object here</span>
            }
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// All methods here</span>
    },
    mounted(){  
        <span class="hljs-comment">// Lifecycle hook      </span>

    }
}
</code></pre>
<p>The Projects route components have a <code>template</code> property that so far holds a basic markup that only spit out an <code>h1</code> title.</p>
<p>After that there is the component's data method that returns an empty object, then an empty methods object and an empty lifecycle hook.</p>
<p>That's all we need, for now, so let's move on and define the rest of the building blocks, the routes, the router and the Vue instances.</p>
<h3 id="heading-how-to-define-the-routes-router-and-vue-instance">How to define the routes, router, and Vue instance</h3>
<p>Now that we have two components to render on our main pages we can move forward to the next steps:</p>
<ul>
<li>define routes</li>
<li>create the router instance</li>
<li>create and mount the Vue instance</li>
</ul>
<p>First, let's define our two routes and link the components.</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Define routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects},
];
</code></pre>
<p>In the code, we have defined a new constant called routes. In it, we defined two routes as an array of objects. </p>
<p>Each object has two properties:</p>
<ul>
<li>path</li>
<li>component</li>
</ul>
<p>The first object is for the homepage. Its path will respond to requests made to our website base URL, like https://fabiopacifici.com/.</p>
<p>Then the component property links this page to the route's component called <code>Home</code> that we defined in the previous step. </p>
<p>The second object is for the projects page. The path responds to requests made to <code>/projects</code> and it's linked to the <code>Projects</code> route component.</p>
<p>Now that we have our routes:</p>
<pre><code class="lang-js">
<span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})
</code></pre>
<p>Above we used the ES6 syntax that allows us to just put the name of the variable holding the routes since it is equal to the name of the property that we needed to use. It's actually the same as writing <code>routes: routes</code>.</p>
<p>Now, we create a Vue instance. Inject the router instance inside it and finally mount the root element. </p>
<pre><code class="lang-js">
<span class="hljs-comment">// create and mount the vue instance</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>Done! We now have everything in place to start building our portfolio and complete the Projects route component.</p>
<h2 id="heading-how-to-build-the-main-projects-route-component">How to Build the Main Projects Route Component</h2>
<p>We will start working on the data object. Here we need to define properties that will hold all our projects once we fetch data from the git hub API.</p>
<h3 id="heading-the-data-object">The data object</h3>
<p>To keep things easier I have intentionally limited results to 20. If you feel this isn't enough you can change the code as you like. </p>
<p>You can implement pagination for your results by increasing the page property that will be passed to the query string or return more results per page by increasing the value of the <code>perPage</code> property.</p>
<pre><code class="lang-js">data() { 
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">projectsList</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">skills</span>: [],
        <span class="hljs-attr">projectsCount</span>: <span class="hljs-number">5</span>,
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">errors</span>: <span class="hljs-literal">false</span>,
        }
    },
</code></pre>
<p>As we learned in the section where we used Axios to fetch data from the GitHub REST API, there are a few properties we need to define. </p>
<p>The component's data function returns an object with a <code>projects</code> property where we will store all projects we fetch from GitHub.</p>
<p>Then we add a <code>projectsList</code> property that holds only a few projects at a time. We will use this property later to implement a very simple
load more feature in combination with the <code>projectsCount</code> property.</p>
<p>Then we have a <code>skills</code> property where we will store all languages used to build our projects. </p>
<p>We'll use the <code>perPage: 20</code> and <code>page: 1</code> properties to build the query string used to fetch data from GitHub. It will take 20 projects and 
return only the first page of results unless we change these values.</p>
<p>Finally, we have a <code>loading: true</code> property that we will use to check if the page is fetching data and an <code>errors: false</code> property that shows an error message in case we are unable to connect to the GitHub server.</p>
<p>In the next step, we will start working on all methods required to make our application work.</p>
<h3 id="heading-the-fetch-all-data-method">The fetch all data method</h3>
<p>The first method is the one we will use to fetch data from GitHub.</p>
<p>This method will make the Ajax call to the GitHub rest API using Axios and store the response in a property of the Vue instance:</p>
<pre><code class="lang-js">
 <span class="hljs-attr">fetchData</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            axios
            .get(<span class="hljs-string">`https://api.github.com/users/fbhood/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
            .then(
                <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {

                    <span class="hljs-built_in">this</span>.projects = response.data;
                    <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
                        <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
                            <span class="hljs-built_in">this</span>.skills.push(project.language)
                        };
                    });
                }
            )
            .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-built_in">this</span>.errors = <span class="hljs-literal">true</span>;
            })
            .finally(<span class="hljs-function">() =&gt;</span> { 
                <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>
                <span class="hljs-built_in">this</span>.getProjects();
            })
        },
</code></pre>
<p>Let's break this down. First, we defined a method called <code>fetchData: function(){}</code>. This method uses Axios to make an API call to the
REST API. </p>
<p>In the <code>.get()</code> method we have built the URL also using the properties <code>perPage</code> and <code>page</code> as part of the query string.</p>
<p>The get method returns a promise so we used the <code>.then()</code> method on the promise to handle the response using an arrow function <code>response =&gt; {}</code>.</p>
<p>Inside the arrow function, we stored the response data inside the projects property of the Vue instance using <code>this.projects = response.data;</code>.</p>
<p>Next, we used a <code>forEach</code> loop to iterate over each project and store the language used in the repository as a skill using the code below:</p>
<pre><code class="lang-js"><span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
    <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
        <span class="hljs-built_in">this</span>.skills.push(project.language)
    };
});
</code></pre>
<p>We chained a <code>.catch</code> method to handle an error in case we are unable to connect to the rest API and fetch data. We will log the error to the
console and update the value of the <code>errors</code> property to true so that we can show a custom error message to the user later on. </p>
<p>Finally, we chained the <code>.finally()</code> method that will be executed after the response has been handled. We also updated the <code>loading</code> property and set it to false so that we can show the results to the user. </p>
<p>Inside the <code>finally</code> method we can also call a method (that we still have to create) and that we will use to slice the results later. </p>
<p>Let's build it.</p>
<h3 id="heading-the-get-projects-method">The get projects method</h3>
<p>This method takes a portion of the projects we actually stored in the <code>projects</code> property. We can use the <code>projectsList</code> property to store the slice and later implement a method to increment them with a show more button.</p>
<pre><code class="lang-js">
<span class="hljs-attr">getProjects</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

    <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.projectsList;

},
</code></pre>
<p>The getProjects method takes a portion of all projects stored in the <code>projects</code> property using the array slice method in conjunction with
the property <code>projectsCount</code> that is set to five. So it will store in there only the first five results and return them.</p>
<p>To add five more projects to the <code>projectsList</code> property we will also need a method that the user can call when he clicks on the load more button. Let's create it.</p>
<h3 id="heading-the-load-more-projects-method">The load more projects method</h3>
<p>The load more method will first check if the length of the <code>projects</code> array is less than or equal to the length of the <code>projectsList</code> array. Then, if not, it will increment the value of the <code>projectsCount</code> property by five and then take a bigger slice from the <code>projects</code> property.</p>
<pre><code class="lang-js">
loadMore(){

    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.projectsList.length &lt;= <span class="hljs-built_in">this</span>.projects.length){
        <span class="hljs-built_in">this</span>.projectsCount += <span class="hljs-number">5</span>;
        <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount)
    }


}
</code></pre>
<h3 id="heading-build-the-template">Build the template</h3>
<p>In the template property of the <code>Projects</code> component, we can start with the header section. We'll also put in there two <code>router-link</code> components for the pages navigation:</p>
<pre><code class="lang-html">`<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"site_header"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container d_flex"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/img/avatar.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media__text"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Python Expert<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. <span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/'</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Project<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</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://"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-github fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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">nav</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">div</span>&gt;</span>
</code></pre>
<p>Next, we can continue working on the template and create the main section.
We will put the following markup always in the main template div, right under the header closing tag.</p>
<p>Let's start by placing the main container:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show an error message if the REST API doensn't work --&gt;</span>
    <span class="hljs-comment">&lt;!-- Otherwise show  a section for our portfolio projects and skills section--&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
</code></pre>
<p>Inside the container, let's use the v-if-else directives to show an error message or the projects section:</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- Show Errors if the rest api doesn't work --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"errors"</span>&gt;</span> 
        Sorry! It seems we can't fetch data righ now 😥
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Else show the portfolio section --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>To make the code work, we used the v-if directive and passed to it the <code>errors</code> property. This property will be set to <code>true</code> if there is an error while we fetch data from GitHub or will be set to <code>false</code> if everything is ok. So the v-else directive will render the portfolio section.</p>
<p>Next, we need to show a 'loading...' message while we fetch data. When done we can use the v-for directive to loop over the results. So right in the portfolio section, we will write another v-if-else directive.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span>
 <span class="hljs-comment">&lt;!-- Use a v-if directive to show the loading message --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loading"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"loading"</span>&gt;</span>😴 Loading ... <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- use a v-for directive to loop over the projectsList array --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> <span class="hljs-attr">v-else</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">section</span>&gt;</span>
</code></pre>
<p>Here we use the v-if directive <code>&lt;div class="loading" v-if="loading"&gt;😴 Loading ... &lt;/div&gt;</code> to render a loading message. After that the <code>&lt;div v-for="project in projectsList" class="card__custom" v-else&gt;&lt;/div&gt;</code>
has two directives, the v-for directive that we use to loop over the <code>projectsList</code> property and a v-else directive that will show this element when we are done fetching data from GitHub.</p>
<p>Now we can use the <code>project</code> variable to render all project details in our markup:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- use a v-for directive to loop over the projectsList array --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> <span class="hljs-attr">v-else</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__text"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Create a custom method to trim the project name so that it doesn't break the design --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{project.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Create a custom trimmedText to trim the description --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{project.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"meta__data d_flex"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"date"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Updated at<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{new Date(project.updated_at).toDateString()}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</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">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</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">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__img"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_custom__button"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
            Code
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>
</code></pre>
<p>To render the project title and desciption we used the propeties <code>poject.name</code> and <code>project.description</code>. But the description and the title will break our design unless we trim them at some point. </p>
<p>Next in the element with class <code>date</code> we rendered the poject data in a readable fomat using the <code>new Data().toDateString()</code> method. </p>
<p>To render the user avatar <code>&lt;img class="avatar" :src="project.owner.avatar_url"&gt;</code> we used the shortcut for the v-bind diective so that we could use the property <code>project.owner.avatar_url</code> to grab the avatar URL. </p>
<p>Finally, to render a button that once clicked redirects the user to the repository page we bound the <code>href</code> attribute to the <code>project.html_url</code> property <code>&lt;a :href="project.html_url" target="_blank"&gt;Code&lt;/a&gt;</code>.</p>
<p>Our project card is complete. The next thing we need to do is render a load more button to show more projects. </p>
<p>We are still working inside the <code>projects</code> section. Right after the project card we can write the following markup</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Render a load more button --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"text-align: center; width:100%"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!loading"</span> &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"projectsList.length &lt; projects.length"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn_load_more"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"loadMore()"</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</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">div</span> <span class="hljs-attr">v-else</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> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Visit My GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>
</code></pre>
<p>The v-if directive first checks if the loading property is set to false. 
If so, we'll use another v-if directive to check if the length of property <code>projectsList</code> is less than the length of the property <code>projects</code>. </p>
<p>If so, it will show a button that uses a v-on directive to listen for clicks 
<code>&lt;button class="btn_load_more" v-on:click="loadMore()"&gt;Load More&lt;/button&gt;</code> and trigger a <code>loadMore()</code> method. Otherwise, we show a link to the GitHub account. </p>
<p>After this, we can show a list of skills related to all the projects:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show a skills section --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills_section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Development Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Loop over the skills property --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"skill in skills"</span>&gt;</span>{{skill}}<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>
</code></pre>
<p>Our markup is complete, but we need to improve our code a little as the project title and its description are breaking our design! </p>
<p>Let's create two methods, one to trim the title and one for the description text.</p>
<h3 id="heading-the-trimtext-and-trimtitle-methods">The trimText and trimTitle methods</h3>
<p>The <code>trimTitle</code> method will replace all <code>-</code> and <code>_</code> with a space, and restrict the number of characters to 12. The <code>trimText</code> method instead only reduces the number of characters of the description in excess of 100 characters. </p>
<pre><code class="lang-js">trimTitle: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
    <span class="hljs-keyword">let</span> title = text.replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">" "</span>).replace(<span class="hljs-string">"_"</span>, <span class="hljs-string">" "</span>)
    <span class="hljs-keyword">if</span>(title.length &gt; <span class="hljs-number">15</span>) {
        <span class="hljs-keyword">return</span> title.slice(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>) + <span class="hljs-string">' ...'</span>
    } <span class="hljs-keyword">return</span> title;

},
<span class="hljs-attr">trimText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
    <span class="hljs-comment">//console.log(text.slice(0, 100));</span>
    <span class="hljs-keyword">if</span>(text.length &gt; <span class="hljs-number">100</span>) {
        <span class="hljs-keyword">return</span> text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">' ...'</span>
    } <span class="hljs-keyword">return</span> text;
},
</code></pre>
<p>With these two methods, now we can update the markup and use them to make sure nothing breaks the design.</p>
<p>Let's update these two lines that will be changed from this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Create a custom method to trim the project name so that it doesn't break the design --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{project.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Create a custom trimmedText to trim the description --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{project.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>To this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{trimedTitle(project.name)}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{trimedText(project.description)}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Let's put eveything together. The final markup will be the following:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"site_header"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container d_flex"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/img/avatar.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media__text"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Python Expert<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. <span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/'</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Project<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</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://"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-github fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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">nav</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">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"errors"</span>&gt;</span> 
            Sorry! It seems we can't fetch data righ now 😥
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loading"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"loading"</span>&gt;</span>😴 Loading ... <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-else</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__text"</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">h3</span>&gt;</span>{{trimedTitle(project.name)}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{trimedText(project.description)}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"meta__data d_flex"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"date"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Updated at<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{new Date(project.updated_at).toDateString()}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</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">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</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">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__img"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_custom__button"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
                            Code
                        <span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>


                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"text-align: center; width:100%"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!loading"</span> &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"projectsList.length &lt; projects.length"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn_load_more"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"loadMore()"</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</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">div</span> <span class="hljs-attr">v-else</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> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Visit My GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills_section"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Development Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"skill in skills"</span>&gt;</span>{{skill}}<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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">section</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>&gt;</span>
</code></pre>
<p>There is one last thing to do. Since fetching data from GitHub is very fast we don't really see the loading message. Let's set a timeout and delay it by a few seconds – then you can tune it as you like.</p>
<h3 id="heading-the-mounted-lifecycle-hook">The mounted lifecycle hook</h3>
<pre><code> mounted(){  

        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">this</span>.fetchData, <span class="hljs-number">3000</span> );

    }
</code></pre><p>Inside the mounted lifecycle hook we used <code>setTimeout()</code> and called the <code>fetchData</code> method as the first parameter. Then for the second parameter we specified that this method should be executed after 3000 milliseconds (3seconds). </p>
<h2 id="heading-lets-see-our-final-code-all-toghether">Let's see our final code all toghether</h2>
<p>Index.html file looks like the following:</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">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-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">title</span>&gt;</span>Vuefolio<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">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Raleway:wght@100;300;400;900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</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">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</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">"./assets/css/style.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">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</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">footer</span>&gt;</span> © Developed by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fabiopacifici.com"</span>&gt;</span>Fabio Pacific<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Main Js file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/js/main.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>And this is the main.js file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create route components</span>
<span class="hljs-keyword">const</span> Home = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;main id="home"&gt;
        &lt;div class="about__me"&gt;
            &lt;img src="./assets/img/avatar.svg" alt=""&gt;
            &lt;h1&gt;John Doe&lt;/h1&gt;
            &lt;h3&gt;Python Expert&lt;/h3&gt;
            &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;

            &lt;div class="skills_projects_link"&gt;&lt;router-link to="/projects"&gt;Projects/Skills&lt;/router-link&gt; &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;`</span>
}
<span class="hljs-keyword">const</span> Projects = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;div&gt;
        &lt;header id="site_header" class="container d_flex"&gt;
            &lt;div class="bio__media"&gt;
                &lt;img src="./assets/img/avatar.svg" alt=""&gt;
                &lt;div class="bio__media__text"&gt;
                    &lt;h1&gt;John Doe&lt;/h1&gt;
                    &lt;h3&gt;Python Expert&lt;/h3&gt;
                    &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;nav&gt;
                &lt;router-link to='/'&gt;Home&lt;/router-link&gt;
                &lt;router-link to="/projects"&gt;Project&lt;/router-link&gt;
                &lt;a href="https://"&gt;
                    &lt;i class="fab fa-github fa-lg fa-fw"&gt;&lt;/i&gt;
                &lt;/a&gt;
            &lt;/nav&gt;
        &lt;/header&gt;

         &lt;main class="container"&gt;
            &lt;div class="error" v-if="errors"&gt; 
                Sorry! It seems we can't fetch data righ now 😥
            &lt;/div&gt;

            &lt;section id="portfolio" v-else&gt;
                &lt;div class="loading" v-if="loading"&gt;😴 Loading ... &lt;/div&gt;
                &lt;div class="projects" v-else&gt;
                     &lt;div v-for="project in projectsList" class="card__custom" &gt;
                        &lt;div class="card__custom__text"&gt;
                            &lt;div&gt;
                                &lt;h3&gt;{{trimedTitle(project.name)}}&lt;/h3&gt;
                                &lt;p&gt;{{trimedText(project.description)}}&lt;/p&gt;                        
                            &lt;/div&gt;

                            &lt;div class="meta__data d_flex"&gt;
                                &lt;div class="date"&gt;
                                    &lt;h5&gt;Updated at&lt;/h5&gt;
                                    &lt;div&gt;{{new Date(project.updated_at).toDateString()}}&lt;/div&gt;
                                &lt;/div&gt;
                                &lt;img class="avatar" :src="project.owner.avatar_url"&gt;

                            &lt;/div&gt;
                        &lt;/div&gt;
                        &lt;div class="card__custom__img"&gt;&lt;/div&gt;
                        &lt;div class="card_custom__button"&gt;
                            &lt;a :href="project.html_url" target="_blank"&gt;
                                Code
                            &lt;/a&gt;
                        &lt;/div&gt;


                    &lt;/div&gt;


                    &lt;div style="text-align: center; width:100%" v-if="!loading" &gt;
                        &lt;div v-if="projectsList.length &lt; projects.length"&gt;
                            &lt;button class="btn_load_more" v-on:click="loadMore()"&gt;Load More&lt;/button&gt;
                        &lt;/div&gt;
                        &lt;div v-else&gt;
                            &lt;a href="" target="_blank" rel="noopener noreferrer"&gt;Visit My GitHub&lt;/a&gt;
                        &lt;/div&gt;

                    &lt;/div&gt;

                    &lt;div id="skills_section"&gt;
                        &lt;h2&gt;Development Skills&lt;/h2&gt;
                        &lt;ul class="skills"&gt;
                            &lt;li v-for="skill in skills"&gt;{{skill}}&lt;/li&gt;
                        &lt;/ul&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/section&gt;


        &lt;/main&gt;
    &lt;/div&gt;`</span>,
data() { 
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">data</span>: [],
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">projectsList</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">skills</span>: [],
        <span class="hljs-attr">projectsCount</span>: <span class="hljs-number">5</span>,
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">errors</span>: <span class="hljs-literal">false</span>,
        }
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-attr">trimedTitle</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
            <span class="hljs-keyword">let</span> title = text.replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">" "</span>).replace(<span class="hljs-string">"_"</span>, <span class="hljs-string">" "</span>)
            <span class="hljs-keyword">if</span>(title.length &gt; <span class="hljs-number">15</span>) {
                <span class="hljs-keyword">return</span> title.slice(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>) + <span class="hljs-string">' ...'</span>
            } <span class="hljs-keyword">return</span> title;

        },
        <span class="hljs-attr">trimedText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
            <span class="hljs-comment">//console.log(text.slice(0, 100));</span>
            <span class="hljs-keyword">if</span>(text === <span class="hljs-literal">null</span>) {
                <span class="hljs-keyword">return</span> <span class="hljs-string">'This project has no description yet!'</span>;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(text.length &gt; <span class="hljs-number">100</span>) {
                <span class="hljs-keyword">return</span> text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">' ...'</span>
            } 
            <span class="hljs-keyword">return</span> text;

        },
        <span class="hljs-attr">getProjects</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

            <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount);
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.projectsList;

        },
        <span class="hljs-attr">fetchData</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            axios
            .get(<span class="hljs-string">`https://api.github.com/users/fbhood/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
            .then(
                <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                    <span class="hljs-built_in">this</span>.projects = response.data;
                    <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
                        <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
                            <span class="hljs-built_in">this</span>.skills.push(project.language)
                        };
                    });
                }
            )
            .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-built_in">this</span>.errors = <span class="hljs-literal">true</span>;
            })
            .finally(<span class="hljs-function">() =&gt;</span> { 
                <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>
                <span class="hljs-built_in">this</span>.getProjects();
            })
        }, 
        loadMore(){

            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.projectsList.length &lt;= <span class="hljs-built_in">this</span>.projects.length){
                <span class="hljs-built_in">this</span>.projectsCount += <span class="hljs-number">5</span>;
                <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount)
            }


        }

    },
    mounted(){  

        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">this</span>.fetchData, <span class="hljs-number">3000</span> );

    }
}

<span class="hljs-comment">// Define routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects},
];


<span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})

<span class="hljs-comment">// create and mount the vue instance</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>On the CSS side this is what we have:</p>
<pre><code class="lang-css">
<span class="hljs-comment">/* Utility Classes */</span>
<span class="hljs-selector-class">.d_none</span> {
    <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-class">.d_flex</span> {
    <span class="hljs-attribute">display</span>: flex;
}
<span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1170px</span>;
    <span class="hljs-attribute">margin</span>: auto;
}
<span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">color</span>: white;
} 
<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>:<span class="hljs-number">#DB5461</span>;

}
<span class="hljs-selector-class">.loading</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-comment">/* END Utility Classes */</span>
<span class="hljs-comment">/* Components */</span>
<span class="hljs-selector-class">.bio__media</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-start;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">text-align</span>: left;
}
<span class="hljs-selector-class">.bio__media</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;
}
<span class="hljs-selector-class">.bio__media__text</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">h1</span>{
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;

}
<span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;

}

<span class="hljs-selector-class">.card__custom</span> {
    <span class="hljs-attribute">position</span>: relative;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
    <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
    <span class="hljs-attribute">flex-basis</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> /<span class="hljs-number">2</span>);
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-between;
}
<span class="hljs-selector-class">.card__custom</span> &gt; <span class="hljs-selector-class">.card__custom__text</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100%</span> / <span class="hljs-number">3</span>) *<span class="hljs-number">2</span>);
    <span class="hljs-attribute">text-align</span>: right;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">justify-content</span>: space-around;
    <span class="hljs-attribute">overflow</span>: hidden;

}
<span class="hljs-selector-class">.card__custom__img</span> {

    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/cards_bg_img.svg);
    <span class="hljs-attribute">background-position</span>: center;
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">background-size</span>: contain;
    <span class="hljs-attribute">display</span>: inline-block;
    <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">60%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">85px</span> <span class="hljs-number">0</span> <span class="hljs-number">100px</span> <span class="hljs-number">25px</span>;

}
<span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span>, <span class="hljs-selector-class">.btn_load_more</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#F1EDEE</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">54px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#3D5467</span>;
}
<span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.btn_load_more</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#324555</span>;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: <span class="hljs-number">1s</span>;
}
<span class="hljs-selector-class">.card__custom__text</span> <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
}
<span class="hljs-comment">/* END Componenet */</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">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">body</span>{
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, Arial, Helvetica, sans-serif;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(<span class="hljs-number">116.82deg</span>, #<span class="hljs-number">3</span>D5467 <span class="hljs-number">0%</span>, #<span class="hljs-number">1</span>A232B <span class="hljs-number">99.99%</span>, #<span class="hljs-number">333333</span> <span class="hljs-number">100%</span>);

}
<span class="hljs-selector-tag">a</span> {
 <span class="hljs-attribute">text-decoration</span>: none;   
}

<span class="hljs-comment">/* Home Page */</span>
<span class="hljs-selector-tag">main</span><span class="hljs-selector-id">#home</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">600px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">28px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;

}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span>  {
    <span class="hljs-attribute">font-style</span>: normal;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">42px</span>;
    <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">0.115em</span>;

}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">22px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-selector-class">.skills_projects_link</span> {
    <span class="hljs-attribute">position</span>: relative;
}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">21px</span>;

}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease-in-out;

}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">::after</span> {
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">margin</span>: auto;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.5s</span> ease-in-out;

}

<span class="hljs-comment">/* Header */</span>
<span class="hljs-selector-id">#site_header</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">justify-content</span>: space-between;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-id">#site_header</span> &gt; <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#e2e2e2</span>;
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
}
<span class="hljs-comment">/* Portfolio Page Section */</span>

<span class="hljs-selector-id">#portfolio</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;

}


<span class="hljs-selector-id">#portfolio</span> <span class="hljs-selector-class">.projects</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-around;
}
<span class="hljs-comment">/* Skills */</span>

<span class="hljs-selector-id">#skills_section</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/skills_bg.svg);
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">background-size</span>: contain;
    <span class="hljs-attribute">background-position</span>: top left;
}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">180px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">44px</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#F1EDEE</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2rem</span>;

}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">120px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;

}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span>  <span class="hljs-selector-tag">li</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">35px</span>;
}



<span class="hljs-selector-class">.avatar</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;

}


<span class="hljs-selector-class">.card__back</span> {
    <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-class">.rotate__card</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate3d</span>(<span class="hljs-number">360</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">180deg</span>);
}
<span class="hljs-comment">/* Site Footer */</span>

<span class="hljs-selector-tag">footer</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
}



<span class="hljs-comment">/* Media Query  */</span>

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">475px</span>) {
    <span class="hljs-selector-class">.card</span> {
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    }
}
</code></pre>
<p>That's it! We are ready to deply our code to production.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/I6hQnWQU4rQ" 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-continuos-deployment-with-bitbucket-and-netlify">Continuos Deployment with BitBucket and Netlify</h2>
<p>The final step is to deploy our projects so that others can see them. To do that we will use two services:</p>
<ul>
<li>BitBucket, a git-based source code repository for hosting (you can use GitHub if you prefer)</li>
<li>Netlify, a web hosting company that provides hosting for websites that have source code files stored in a Git version control system.</li>
</ul>
<p>You can watch this final video on <a target="_blank" href="https://youtu.be/BH5I68DzcYQ">YouTube here</a>.</p>
<p>You can also checkout the final repositories on BitBucket: </p>
<ul>
<li><a target="_blank" href="https://bitbucket.org/fbhood/simple-twitter/src/master/">SimpleTwitter</a></li>
<li><a target="_blank" href="https://bitbucket.org/fbhood/vue-folio/src">VuePortfolio</a></li>
</ul>
<h3 id="heading-create-the-project-folders-and-copy-all-files">Create the project folders and copy all files</h3>
<p>First, create two folders – one for each project:</p>
<ul>
<li>vue-folio</li>
<li>simple-twitter
then copy all projects files in the related folder.</li>
</ul>
<h3 id="heading-initialize-a-git-repository">Initialize a git repository</h3>
<p>Next, we need to initialise the git repository locally.</p>
<pre><code>cd vue-folio 
git init
git add .
git commit -m<span class="hljs-string">"Initial Commit"</span>
</code></pre><p>You need to execute the commands above in a termial, so you need to have git installed on you system. If you don't, you can read about how to do that <a target="_blank" href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">here</a>.</p>
<p>With the first command, we navigate to the project folder called <code>vue-folio</code>. Then we initialise a git repository, add all files to the staging area, and commit the files.</p>
<p>Repeat the steps above for both projects folders.</p>
<h3 id="heading-create-a-bitbucket-or-github-repository">Create a BitBucket or GitHub repository</h3>
<p>I assume you already have an account with GitHub o BitBucket. But if you don't, then go over and create one.</p>
<p>Follow the steps in the video to create the repositories and connect them with you local repositories.</p>
<h3 id="heading-create-an-account-with-netlify-and-create-a-site">Create an account with Netlify and create a site</h3>
<p>You can use Netlify's free plan for private projects, hobby websites, and experiments. It's a perfect fit for our tutorial. </p>
<p>Follow the steps in the video to deploy your projects there.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/BH5I68DzcYQ" 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-whats-next">What's Next?</h2>
<p>In an upcoming tutorial I'll show you also how to test your code, upgrade to Vue3, and more.</p>
<h3 id="heading-thank-you-for-reading">Thank you for reading!</h3>
<p>I hope you enjoyed this tutorial and the accompanying videos. If so, please share the article and hit the like button on the videos. You can also enable notifications by clicking on the bell icon to know when my next video is online. </p>
<p>If you have any questions please just reach out to me. I reply to all YouTube comments. </p>
<p>Dont forget to subscribe to my YouTube Channel <a target="_blank" href="https://youtube.com/channel/UCTuFYi0pTsR9tOaO4qjV_pQ">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a React Router-Based Application to Netlify ]]>
                </title>
                <description>
                    <![CDATA[ In this article, we'll learn the most popular ways of deploying a React app to Netlify. We'll also learn the configuration changes you'll need to make to deploy a Routing-based React app. The thing I love about Netlify is that it provides a lot of us... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-react-router-based-app-to-netlify/</link>
                <guid isPermaLink="false">66bc5519d94fa6cb67b844ff</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react router ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yogesh Chavan ]]>
                </dc:creator>
                <pubDate>Mon, 19 Apr 2021 21:38:40 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/04/routing.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, we'll learn the most popular ways of deploying a React app to Netlify. We'll also learn the configuration changes you'll need to make to deploy a Routing-based React app.</p>
<p>The thing I love about <a target="_blank" href="https://www.netlify.com/">Netlify</a> is that it provides a lot of useful features<br>for free such as:</p>
<ul>
<li>A way to deploy a static website within seconds</li>
<li>Continuous deployment, which means when you connect your Github/Gitlab/Bitbucket repository, it automatically triggers deployment when new commits are pushed to the repository</li>
<li>Assurance that your website never goes down, even during new deployments</li>
<li>Allows you to easily rollback to any previous working version of your site with a single click</li>
<li>Lets you quickly preview any of the previously deployed versions of the app</li>
<li>Change the domain or subdomain of your site instantly </li>
</ul>
<p>and much more.</p>
<p>So let's see how to deploy a React app to Netlify.</p>
<blockquote>
<p>Want to learn Redux from the absolute beginning and build a food ordering app from scratch? Check out the <a target="_blank" href="https://master-redux.yogeshchavan.dev/">Mastering Redux</a> course.</p>
</blockquote>
<h2 id="heading-drag-and-drop-the-build-folder-in-netlify">Drag and Drop the Build Folder in Netlify</h2>
<p>The fastest and easy way to deploy a React application is just to drag and drop the build folder in Netlify.</p>
<p>To create a build folder, just execute the <code>npm run build</code> or <code>yarn build</code> command from the command line from your project folder.</p>
<p>Once the build folder is created, you just need to drop the folder in the drop area under the <code>sites</code> menu, as shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/drag_drop.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-deploy-an-app-to-netlify-from-a-github-repository">How to Deploy an App to Netlify from a GitHub Repository</h2>
<p>This is my favorite way of deploying applications on Netlify.</p>
<p>Because whenever you push any changes to the GitHub repository, it will automatically be deployed to Netlify. You can also see all deployed versions and easily roll back to any previously working version of code with just a single click.</p>
<p>If you already have a repository pushed to GitHub, then you just need to connect it.</p>
<p>Login to your Netlify account. In the dashboard, click on the <code>New site from Git</code> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/netlify_home.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on the <code>GitHub</code> button to connect your GitHub repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/git_provider.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>It will open a new tab. Make sure the popup is enabled in your browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/select_repository.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Search for the GitHub repository in the <code>Search repos</code> search box. If your repository is not getting displayed then click on the <code>Configure the Netlify app on GitHub</code> button at the bottom of the page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/configure_netlify.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once clicked, scroll down on the page and click on the <code>Select repositories</code> dropdown and search for your repository and click on the <code>Save</code> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/select_repo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will be redirected to the previous page showing all the available repositories.</p>
<p>Search for the repository you want to deploy. For this article, I have selected the <a target="_blank" href="https://github.com/myogeshchavan97/react-book-management-app">react-book-management-app</a> repository which we created in my <a target="_blank" href="https://www.freecodecamp.org/news/react-crud-app-how-to-create-a-book-management-app-from-scratch/">previous article</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/find_repository-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once you select the repository, you will see the following screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/deploy_repository.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For this application, we don't need to change anything.</p>
<p>Your <code>Build command</code> and <code>Publish directory</code> will be automatically populated. Make sure to enter these fields if you have a different command in <code>package.json</code> to build your app or those fields are not auto-populated. </p>
<p>Now, click on the <code>Deploy site</code> button. Once clicked, you will see the <code>Site deploy in progress</code> message.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/deploying.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You'll have to wait a little bit while it's deploying. Once deployment is completed, you will see the following screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/deployed.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Open the link in the new tab and you will see your application deployed live.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/deployed_app.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Awesome! Now, if you make any changes in the source code and push that change to GitHub, Netlify will detect that change and re-deploy your application with your latest changes.</p>
<p>If you check the application, you will see that the application works just fine with the navigation and you're able to add/edit/delete a book.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/working_app.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>But there is one issue.</strong> If you directly access the <code>/add</code> route or refresh the <code>/add</code> route page, you will get a page not found error as shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/page_not_found.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will get the same error if you try to refresh the edit page route.</p>
<p>This is because when we access any route on our local machine, React Router handles the routing. But when we deploy the application on any server, directly accessing the route will send the request to the server itself (Netlify in our case).</p>
<p>But as there is no <code>/add</code> route handler on the server-side, you will see a page not found error. But Netlify provides a way to fix this.</p>
<p>Create a new file with the name <code>_redirects</code> inside the <code>public</code> folder of our project and add the following contents inside it:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* /index.html 200</span>
</code></pre>
<p>Here, we're telling Netlify to redirect all the routes to the <code>index.html</code> file. </p>
<p>The <code>index.html</code> file contains our entire React app code. It gets generated inside the <code>build</code> folder when the <code>yarn build</code> command is executed by Netlify while deploying the app.</p>
<p>And as routing is handled by our React app which is contained in the <code>index.html</code> file, our application will work without a page not found issue.</p>
<p>Now, push the changes to the GitHub repository so Netlify will deploy the app again with these changes.</p>
<p>And once deployed, if you check the deployed application, you will see that the application works fine and we don't get a page not found error.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/no_page_not_found.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>That's it! We're all done with deploying our application to Netlify.</p>
<h2 id="heading-how-to-easily-change-a-site-name-in-netlify">How to Easily Change a Site Name in Netlify</h2>
<p>If you check the name of the deployed site you will see that it's not easy to remember, especially if you have lot of applications deployed. But Netlify provides a way to easily change that.</p>
<p>Click on the <code>Site settings</code> button displayed on the <code>Site overview</code> section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/site_settings.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then click on the <code>Change site name</code> button and enter a new name. Click on the <code>Save</code> button, and now you can access your application with the changed name.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/changed_site_name.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<blockquote>
<p>I usually like to give the same name as the repository name so it's easy to find a particular application if you have a lot of deployed applications on Netlify.</p>
</blockquote>
<p>If you want to know how to deploy React + Node.js application to production, check out <a target="_blank" href="https://dev.to/myogeshchavan97/how-to-deploy-react-node-js-application-to-heroku-4jb4">this article</a>.</p>
<h3 id="heading-thanks-for-reading">Thanks for reading!</h3>
<p>You can find the complete GitHub source code along with this redirection change in <a target="_blank" href="https://github.com/myogeshchavan97/netlify-react-book-management-app">this repository</a>.</p>
<p><strong>You can see the live demo of the deployed application <a target="_blank" href="https://react-book-management-app.netlify.app/">here</a>.</strong></p>
<p>Want to learn all ES6+ features in detail including let and const, promises, various promise methods, array and object destructuring, arrow functions, async/await, import and export and a whole lot more from scratch?</p>
<p>Check out my <a target="_blank" href="https://modernjavascript.yogeshchavan.dev/">Mastering Modern JavaScript</a> book. This book covers all the pre-requisites for learning React and helps you to become better at JavaScript and React.</p>
<blockquote>
<p>Check out free preview contents of the book <a target="_blank" href="https://www.freecodecamp.org/news/learn-modern-javascript/">here</a>.</p>
</blockquote>
<p>Also, you can check out my <strong>free</strong> <a target="_blank" href="https://yogeshchavan1.podia.com/react-router-introduction">Introduction to React Router</a> course to learn React Router from scratch.</p>
<p>Want to stay up to date with regular content regarding JavaScript, React, Node.js? <a target="_blank" href="https://www.linkedin.com/in/yogesh-chavan97/">Follow me on LinkedIn</a>.</p>
<p><a href="https://bit.ly/3w0DGum" target="_blank"><img src="https://gist.github.com/myogeshchavan97/98ae4f4ead57fde8d47fcf7641220b72/raw/c3e4265df4396d639a7938a83bffd570130483b1/banner.jpg" width="600" height="400" alt="banner" loading="lazy"></a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add a Netlify Form to a React App Built with create-react-app ]]>
                </title>
                <description>
                    <![CDATA[ If you are a web developer, at some point you will need to capture information from people who use your website or app. One way of doing so is by using HTML forms. But there are also tons of frameworks out there that you can use to build web apps ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-a-netlify-form-to-a-react-app/</link>
                <guid isPermaLink="false">66d45f6536c45a88f96b7ce3</guid>
                
                    <category>
                        <![CDATA[ create-react-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ forms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joseph Mawa ]]>
                </dc:creator>
                <pubDate>Mon, 19 Apr 2021 21:20:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/04/questions-4304981_1280.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you are a web developer, at some point you will need to capture information from people who use your website or app.</p>
<p>One way of doing so is by using HTML forms. But there are also tons of frameworks out there that you can use to build web apps very quickly.</p>
<p>One such framework is React. You can bootstrap a single page application (SPA) very easily using <code>create-react-app</code> (CRA). Then you can deploy it to platforms such as Netlify, Vercel, Firebase and Digital Ocean in just a couple of steps.</p>
<p>The main focus of this article will be on how to add Netlify form functionality to a React app bootstrapped using <code>create-react-app</code>. At the end of this tutorial you will be able to:</p>
<ul>
<li><p>Quickly set up a single page app using <code>create-react-app</code></p>
</li>
<li><p>Add functionality to utilize Netlify's builtin form handling feature</p>
</li>
<li><p>Deploy the app to Netlify</p>
</li>
<li><p>Configure the builtin form handling feature on Netlify to send email notifications whenever a form has been submitted by a client</p>
</li>
</ul>
<p>Whether you are beginner trying to deploy your first React app or you are an experienced React developer, this article will help you learn to use Netlify's builtin form functionality without writing any server side code.</p>
<p>If you are an experienced React developer, you can skip the introduction and go to <code>step 6</code>. If you are a beginner in React, you can follow along right from the beginning.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this article, you should:</p>
<ul>
<li><p>Have an intermediate knowledge of JavaScript. If you are a beginner, you can still follow along and ask questions on the <a target="_blank" href="https://forum.freecodecamp.org">freeCodeCamp forum</a> if there is something you don't understand. You can also copy the code samples in each section and play with them in your text editor to make sense of what is going on.</p>
</li>
<li><p>Have at least basic knowledge of the React library</p>
</li>
<li><p>Have Node installed on your machine</p>
</li>
<li><p>Have a Netlify account. If you don't have one, you can signup for an account at no cost using your email address.</p>
</li>
<li><p>Have a text editor like <a target="_blank" href="https://code.visualstudio.com/">VS code</a> or <a target="_blank" href="https://atom.io/">Atom</a> installed on your machine. You can try out the code samples as you follow along. It will make it easier for you to understand.</p>
</li>
</ul>
<h2 id="heading-step-1-check-whether-you-have-node-and-npm-installed-on-your-machine">Step 1: Check whether you have <code>node</code> and <code>npm</code> installed on your machine</h2>
<p>Before we get started, you should check whether you have <a target="_blank" href="https://nodejs.org/en/">node</a> installed on your machine.</p>
<p>Node is a JavaScript runtime environment, and it's important to have it installed to be able to run the project. Open a terminal and type the following command in the command prompt.</p>
<pre><code class="lang-js">node - v
</code></pre>
<p>Instead of the above command, you can also type the command below. Both of them do the same thing.</p>
<pre><code class="lang-js">node --version
</code></pre>
<p>If Node is installed, you should be able to see the version printed on the terminal. Your version might be different from mine but you should see something like:</p>
<pre><code class="lang-js">v15<span class="hljs-number">.13</span><span class="hljs-number">.0</span>
</code></pre>
<p>If Node is installed, that means <code>npm</code> is also installed because recent versions of Node come with <code>npm</code>. If you are curious, type the command <code>npm --version</code> or <code>npm -v</code>. You should be able to see the version of <code>npm</code> which has been installed.</p>
<p>On the other hand, if you don't have Node installed on your machine, you can download and install for your platform from <a target="_blank" href="https://nodejs.org/en/download/">here</a>.</p>
<h2 id="heading-step-2-navigate-to-the-directory-where-you-want-to-create-your-project">Step 2: Navigate to the directory where you want to create your project</h2>
<p>Next, you need to navigate to a directory where you want to create your project. You can work from the Desktop or from any directory of your choice.</p>
<p>I like keeping my personal projects in a directory called <code>projects</code> on the desktop for easy access. This is just personal choice.</p>
<p>Open the terminal and navigate to the directory where you want to create your project. I am using <code>cd</code> (change directory) in the commands below.</p>
<p><strong>Take note</strong>: I already have a directory named <code>projects</code> on the Desktop. If you don't, you will have to first run the command <code>mkdir projects</code> before you <code>cd</code> into it. Like I said above, you can decide to work from another directory and then you won't have to run the commands below.</p>
<ol>
<li><p><code>cd Desktop</code></p>
</li>
<li><p><code>cd projects</code></p>
</li>
</ol>
<h2 id="heading-step-3-how-to-bootstrap-a-single-page-app-using-create-react-app">Step 3: How to bootstrap a single page app using <code>create-react-app</code></h2>
<p>How we are going to bootstrap a React project using <code>create-react-app</code>. In the directory where you want to create your project, run the command below.</p>
<pre><code class="lang-js">npx create-react-app netlify-form
</code></pre>
<p>I have named the project <code>netlify-form</code>. You can give it a different name if you wish.</p>
<p>If you don't have <code>create-react-app</code> installed, you will see a prompt on the terminal asking whether it should be installed. Type <code>Y</code> on the command prompt (for "Yes"). It will install <code>create-react-app</code> and then create a React project in the <code>netlify-form</code> directory.</p>
<p>If you already have <code>create-react-app</code> in your system, it will go straight to creating a React project in the <code>netlify-form</code> directory. This will take a couple of minutes, so just be patient.</p>
<p>In the next step, you will start the development server.</p>
<h2 id="heading-step-4-start-the-development-server">Step 4: Start the development server</h2>
<p>In this step, we are going to start the development server. This ensures hot reloading when we make changes to the project during development so that we are able to see how our project is taking shape.</p>
<p>You can open the <code>netlify-form</code> directory in your text editor of choice. When you are in <code>netlify-form</code>, open the terminal and run the command below.</p>
<pre><code class="lang-js">npm run start
</code></pre>
<p>The above command starts the development server on port 3000. If there is another project or service running on port 3000, you will be prompted to start the server on another port.</p>
<p>A new browser tab will be opened in your default browser where you can see the project. Any changes you make will automatically be reflected in the browser.</p>
<p>In the next step, you are going to create a component which will contain your form.</p>
<h2 id="heading-step-5-create-a-new-component-inside-the-src-directory">Step 5: Create a new component inside the <code>src</code> directory</h2>
<p>Now you are going to create a component named <code>Form</code> inside the <code>src</code> directory. In this component, you will have the form which will be rendered in your app.</p>
<p>Create a <code>Form.js</code> file in the <code>src</code> directory then copy and paste the code below in it:</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">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Form</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">br</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">"name"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">required</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">required</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>&gt;</span>Message<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">required</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Submit message"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
  );
}
</code></pre>
<p>The above component returns an ordinary form. I have enclosed each label-input pair and label-textarea pair in its own <code>p</code> tag.</p>
<p>There is nothing special about the <code>p</code> tag. You can use <code>div</code> if you wish. I just used it because I want to apply spacing between successive label-input pairs without using CSS.</p>
<p>You can import the <code>Form</code> component and render it inside <code>App</code>. To clean up <code>App.js</code>, you can also delete some items which come with <code>create-react-app</code>, so that it looks like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"./Form"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></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">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Get in touch <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Form</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>You can also clean up <code>App.css</code> so that it has only the following CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.App</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1em</span>;
}
</code></pre>
<p>When you check your form in the browser it should look like the image below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screenshot-from-2021-04-17-14-30-11.png" alt="Screenshot-from-2021-04-17-14-30-11" width="600" height="400" loading="lazy"></p>
<p>Right now deploying that app to Netlify won't enable us to capture a client's form submissions. To do that, we need to add necessary information to our app so that Netlify's bots will be able to detect our form.</p>
<p>In the next step, we are going to add all the necessary information to make the JSX <code>form</code> in React detectable by Netlify.</p>
<h2 id="heading-step-6-add-necessary-information-to-make-the-form-detectable-by-netlifys-bots">Step 6: Add necessary information to make the <code>form</code> detectable by Netlify's bots</h2>
<p>In this step, you are going to add some information to your app so that Netlify will be able to detect your app's form setup. If your form is deployed in plain HTML, the process of making it detectable is quite simple. You can read about it in the <a target="_blank" href="https://docs.netlify.com/forms/setup/?_ga=2.214149207.1369394306.1618461268-796209470.1617367540">documentation</a>.</p>
<p>But if you are dealing with a JSX form in React like in this simple app we are building, then you will have to do a bit more work. You can follow the steps outlined below.</p>
<h3 id="heading-add-the-html-version-of-the-form-to-the-indexhtml-file">Add the HTML version of the form to the <code>index.html</code> file</h3>
<p>Copy and paste your JSX form into the <code>index.html</code> file just after the opening <code>body</code> tag. This will ensure that Netlify detects our form because build bots parse the HTML files directly at deploy time. The JSX form cannot be parsed by the bots.</p>
<p>You can remove the <code>label</code> elements and the <code>submit</code> input element because we will add a <code>hidden</code> attribute to the <code>form</code> so that it is not visible to users and screen readers.</p>
<p>You can only leave the <code>type</code> and <code>name</code> attributes on the <code>input</code> elements and <code>name</code> attribute on <code>textarea</code> so that we keep the form minimal.</p>
<p>This is illustrated in the code below:</p>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">netlify</span> <span class="hljs-attr">netlify-honeypot</span>=<span class="hljs-string">"bot-field"</span> <span class="hljs-attr">hidden</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">name</span>=<span class="hljs-string">"name"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>As you can see in the above code snippet, there are additional attributes <code>netlify</code> and <code>netlify-honeypot</code> on the <code>form</code>. Netlify bots will use them while parsing your HTML so make sure to add them.</p>
<p>Do not forget to add the <code>hidden</code> attribute, because this form needs to be hidden from the users of your website. It is also worth pointing out that the name attributes in the HTML form must be exactly the same as those in the corresponding JSX form.</p>
<h3 id="heading-add-a-hidden-input-element-in-your-jsx-form">Add a hidden <code>input</code> element in your JSX form</h3>
<p>You also need to add a hidden <code>input</code> element in your JSX form with the attributes <code>name</code> and <code>value</code> as illustrated in the code below:</p>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"form-name"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"contact"</span> /&gt;</span>
</code></pre>
<p>The value of the <code>name</code> attribute should always be <code>"form-name"</code> and the value of the <code>value</code> attribute should be the name of the HTML form, which in our case is <code>contact</code>.</p>
<p>Your <code>Form.js</code> file should now look like this:</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">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Form</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"form-name"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"contact"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">br</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">"name"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">required</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">required</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>&gt;</span>Message<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">required</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Submit message"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
  );
}
</code></pre>
<p>If you check the app in the browser, you should be able to see the form – but you won't be able to submit it from your local setup. You can only submit forms after deploying your app to Netlify.</p>
<p>So let's do that now.</p>
<h2 id="heading-step-7-deploy-the-app-to-netlify">Step 7: Deploy the app to Netlify</h2>
<p>In this step, you are going to deploy our app to Netlify so that you are able to test whether clients can make form submissions.</p>
<p>There are a couple of ways you can deploy your app to Netlify. One method is by building the app locally and deploying it from the command line, or by dragging and dropping the production build to Netlify. The second method is by configuring automatic deployment via GitHub, BitBucket, or GitLab.</p>
<p>In this app, you will build the app locally and use the simplest method of dragging and dropping. This step requires you to login to your Netlify account. If you don't have an account, you can signup for one.</p>
<p>Run the command <code>npm run build</code> in the terminal. This will build the app for production to the <code>build</code> folder. This will take a bit of time. You should be able to see the <code>build</code> directory after running the command successfully.</p>
<p>Login to your Netlify account. On the Netlify dashboard, click the <code>Sites</code> menu item. At the bottom of the page, there is an area where you can drag and drop the production build of your app. After dragging and dropping the <code>build</code> folder, the build process begins.</p>
<p>After the site is built successfully, you can check the project dashboard to find out whether Netlify is detecting your form. If it is detecting the form, you will usually see a message stating so in the forms section at the bottom left.</p>
<p>What is left for you to do is to fill out the form and submit it. After submission, you should be able to see the information submitted.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screenshot-from-2021-04-17-14-03-26.png" alt="Screenshot-from-2021-04-17-14-03-26" width="600" height="400" loading="lazy"></p>
<p>Next, we'll learn how to configure email alerts so you get notified whenever a user submits a form.</p>
<h2 id="heading-step-8-configure-email-updates-whenever-a-user-submits-a-form">Step 8: Configure email updates whenever a user submits a form</h2>
<p>In this section, you will configure your app to send email notifications to an email address whenever a form is submitted.</p>
<p>To do this, navigate to the site settings. On the left, you will see a list of menu items. Click the <code>forms</code> option.</p>
<p>Under <code>outgoing notifications</code>, click <code>Add notification</code> and select the <code>Email notification</code> option. You will then configure your preferences accordingly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screenshot-from-2021-04-17-14-11-20.png" alt="Screenshot-from-2021-04-17-14-11-20" width="600" height="400" loading="lazy"></p>
<p>That is all you need to do to use Netlify's builtin form functionality with CRA. You don't need server side code to get feedback from your clients.</p>
<p>If you have managed to successfully follow the above steps, congratulations! You can now go ahead and explore other features.</p>
<p>If you encounter errors or issues relating to Netlify forms while following this tutorial, feel free to check out <a target="_blank" href="https://answers.netlify.com/t/support-guide-form-problems-form-debugging-404-when-submitting/92">this netlify form error debugging tip</a>.</p>
<p>You can also read the <a target="_blank" href="https://docs.netlify.com/forms/setup/">netlify forms documentation</a>.</p>
<p>If you fail to find a solution after using the above resources, you can ask a quesion in the <a target="_blank" href="https://answers.netlify.com/">netlify forum</a>. There are quite a number of friendly folks in that community who might help you out.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we look looked at:</p>
<ul>
<li><p>How to create a React app using <code>create-react-app</code></p>
</li>
<li><p>How to add a JSX form to your React app</p>
</li>
<li><p>How to add necessary information so that your form can be detected by Netlify's bots</p>
</li>
<li><p>How to deploy a production build to Netlify</p>
</li>
<li><p>How to set up email notifications whenever a client submits a form</p>
</li>
</ul>
<h3 id="heading-references">References</h3>
<ul>
<li><p><a target="_blank" href="https://docs.netlify.com/forms/setup/">Netlify forms documentation</a></p>
</li>
<li><p><a target="_blank" href="https://create-react-app.dev/">Create react app documentation</a></p>
</li>
<li><p><a target="_blank" href="https://reactjs.org/docs/getting-started.html">React documentation</a></p>
</li>
<li><p><a target="_blank" href="https://answers.netlify.com/t/support-guide-form-problems-form-debugging-404-when-submitting/92">Netlify form error debugging</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Blog Using a Static Site Generator and a CDN ]]>
                </title>
                <description>
                    <![CDATA[ By Aaron Katz I wanted to set up a fun project for myself to learn some new technologies. And this time I decided I wanted to learn a bit about Static Site Generators (SSGs). My goal was to build a blog using an SSG and have it deploy any time ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-blog-using-a-static-site-generator-and-a-cdn/</link>
                <guid isPermaLink="false">66d45d5fbd438296f45cd387</guid>
                
                    <category>
                        <![CDATA[ Blogging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ content delivery network  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 29 Jan 2021 16:46:23 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c95e0740569d1a4ca0ebe.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Aaron Katz</p>
<p>I wanted to set up a fun project for myself to learn some new technologies. And this time I decided I wanted to learn a bit about <strong>Static Site Generators</strong> (SSGs).</p>
<p>My goal was to build a blog using an SSG and have it deploy any time the code repository changed. You can see the result at <a target="_blank" href="https://caliburnsecurity.com">https://caliburnsecurity.com</a>.</p>
<h2 id="heading-blog-requirements">Blog Requirements</h2>
<p>Below are the requirements I put together when determining what I wanted my blog to support.</p>
<ul>
<li>Support Markdown for content generation</li>
<li>Syntax highlighting</li>
<li>Code line numbering</li>
<li>"Serverless"</li>
<li>Ready made themes/plugins - I am so NOT a frontend developer</li>
<li>SEO capabilities</li>
<li>Browse by keyword/category</li>
<li>Search support (because this is statically generated, searching is through Google's index - there are other articles that discuss how to add a dynamic JavaScript powered search)</li>
<li>RSS support</li>
<li>Version controlled</li>
<li>Static - no dynamic content (with a neat side effect of shrinking the site's attack surface and improving security)</li>
</ul>
<h2 id="heading-so-what-actually-is-a-static-site-generator">So what actually is a Static Site Generator?</h2>
<p>In short, an SSG is a framework designed to manage your website and transform it into a site serving only static pages.</p>
<h2 id="heading-why-use-a-ssg-to-build-the-blog">Why use a SSG to build the blog?</h2>
<p>One thing that took me some time to comprehend was why I would use a static site, if I already had a CMS. There were so many articles online about using an SSG with a headless CMS...but why?  </p>
<p>Best I can tell, the benefits are simply that you have more flexibility using familiar frameworks such as React or Vue, while using a CMS to handle all content. </p>
<p>However, as I am by no means a frontend developer, I was close to scrapping this project. I thought "Oh well, I should just use Ghost - it's only $5/month if hosted on a platform like DigitalOcean, and is an all-in-one platform to serve content as well as manage the content".</p>
<p>However, I really wanted to try to learn something new, and see if I could just deploy a blog for free using only Markdown.</p>
<p>As always ends up happening, what I was hoping would take a few hours took quite a bit more time as I went down the rabbit hole of research into various technologies. </p>
<p>I played around with several different technologies, such as (not all of these are SSGs, more on that later):</p>
<ul>
<li>Pelican (Python)</li>
<li>Hugo (Go)</li>
<li>Hexo</li>
<li>Gridsome (Vue - JS)</li>
<li>VuePress (Vue - JS)</li>
<li>Ghost</li>
<li>Gatsby (React - JS)</li>
<li>Jekyll (Ruby)</li>
</ul>
<h3 id="heading-why-i-chose-hugo">Why I Chose Hugo</h3>
<p>I won't go in too much detail on all of the technologies I looked at. But in general, I found Hugo to be super quick to set up and build, and just the <strong>SIMPLEST</strong> of all the options.  </p>
<p>While I know this is similar to Jekyll, I really just didn't want to deal with configuring a Ruby environment, and the speed of Hugo left everything else in the dust.</p>
<h2 id="heading-how-to-start-building-the-blog">How to Start Building the Blog</h2>
<p>For this exercise, let's build a static blog hosted by Netlify (free!).</p>
<p>Note: I'll be using PowerShell on my Windows box for this tutorial, so please recall that if copy/pasting.</p>
<h3 id="heading-make-sure-you-have-these-dependencies">Make sure you have these dependencies:</h3>
<ul>
<li>Git</li>
<li>Visual Studio Code (or your preferred editor)</li>
<li>Hugo binary</li>
</ul>
<h3 id="heading-heres-a-high-level-overview-of-our-workflow">Here's a high level overview of our workflow:</h3>
<ol>
<li>Download/Install Hugo</li>
<li>Create a Hugo project</li>
<li>Add and configure a theme</li>
<li>Add to Git</li>
<li>Deploy to Netlify</li>
<li>(Optional) Configure the free Netlify CMS</li>
</ol>
<h3 id="heading-download-or-install-hugo">Download or install Hugo</h3>
<p>To <a target="_blank" href="https://gohugo.io/getting-started/installing/">install Hugo</a>, I went over to their <a target="_blank" href="https://github.com/gohugoio/hugo/releases">GitHub Releases</a> page and downloaded their standalone Windows x64 binary. I placed it in my Projects directory, where we will be creating our site (you can always install it properly/add the binary to your PATH, but I wanted quick).</p>
<h2 id="heading-how-to-create-the-hugo-site">How to Create the Hugo Site</h2>
<p>To create a new site, simply run the below commands:</p>
<pre><code class="lang-powershell">.\hugo.exe new site hugo<span class="hljs-literal">-blog</span>
<span class="hljs-built_in">mv</span> .\hugo.exe .\hugo<span class="hljs-literal">-blog</span>
<span class="hljs-built_in">cd</span> .\hugo<span class="hljs-literal">-blog</span>
.\hugo.exe server <span class="hljs-literal">-D</span> -<span class="hljs-literal">-gc</span>
</code></pre>
<p>We now have our project created, and have just started the Hugo server. We used the <strong>-D</strong> flag to tell Hugo to show draft content, and I typically add in <strong>--gc</strong> to ensure that cleanup is run each time by clearing the cache.  </p>
<p>You can access your site at http://localhost:1313.</p>
<h3 id="heading-understanding-the-directory-structure">Understanding the directory structure</h3>
<p>You should now see the following directory structure:</p>
<pre><code class="lang-shell">|__archetypes
|__assets *this will not show up by default
|__config *this will not show up by default
|__content
|__data
|__layouts
|__static
|__themes
|__config.toml
</code></pre>
<ul>
<li><strong>archetypes</strong>: Content template files with pre-configured front matter. We won't really be touching this.</li>
<li><strong>assets</strong>: Store any files processed by <a target="_blank" href="https://gohugo.io/hugo-pipes/">Hugo Pipes</a>. This is out of scope for this tutorial.</li>
<li><strong>config</strong>: Optional directory to store configuration files. We won't be doing anything too crazy, so we will just use the default config.toml file.</li>
<li><strong>content</strong>: This is where your content lives - your posts and pages. The top level folders within this directory are treated as a <em>section</em>.</li>
<li><strong>data</strong>: Contains configuration files used by Hugo. I never had a need to touch this directory.</li>
<li><strong>layouts</strong>: Stores partial or full page HTML templates for the site. Anything in here can overwrite a corresponding entry from your theme, allowing you to customize the theme without modifying the theme's actual files.</li>
<li><strong>static</strong>: Store any static content, such as images, CSS, or scripts. Anything in this folder is copied as-is, without any modification or interpretation by Hugo. This is where you will store your media, such as images, for reference in your blog posts.</li>
<li><strong>themes</strong>: The directory where you will install any Hugo themes.</li>
<li><strong>config.toml</strong>: The default site configuration. You can use a separate directory if you want to separate different environments.</li>
</ul>
<h2 id="heading-how-to-add-your-first-theme">How to Add Your First Theme</h2>
<p>For this blog, we will use the <a target="_blank" href="https://github.com/EmielH/tale-hugo">tale</a> theme for Hugo. Run the following commands from the root of the project:</p>
<pre><code class="lang-powershell">git submodule add https://github.com/EmielH/tale<span class="hljs-literal">-hugo</span>.git .\themes\tale
</code></pre>
<p>We will <strong>NOT</strong> be editing any files from the theme, but will make all modifications in the <strong>layouts</strong> folder discussed above. This will let us always update the submodule to update our theme without worry that we will overwrite any changes we have made.</p>
<p>To initialize the theme, edit the <strong>config.toml</strong> in your root directory and add the following lines (while also editing the defaults):</p>
<pre><code class="lang-toml"><span class="hljs-comment"># Theme Settings</span>
<span class="hljs-attr">theme</span> = <span class="hljs-string">"tale"</span>
<span class="hljs-section">[params]</span>
  <span class="hljs-attr">Author</span> = <span class="hljs-string">"Aaron Katz"</span> <span class="hljs-comment"># Add the name of the author (this theme only supports one author)</span>
<span class="hljs-section">[author]</span>
  <span class="hljs-attr">name</span> = <span class="hljs-string">"Caliburn Security"</span> <span class="hljs-comment"># Used by the foot copyright</span>
</code></pre>
<p>There we go, the theme is now active! (Note that in many cases, themes will require you to copy and paste the theme's <strong>theme.toml</strong> file into your <strong>config.toml</strong>)</p>
<p>Go ahead and check out your page – every time you save a file Hugo rebuils the site live.</p>
<h3 id="heading-how-to-modify-theme-files">How to modify theme files</h3>
<p>One issue with the current theme is that non-post content will be displayed in the homepage list.  </p>
<p>To change this, let's copy the <strong>.\themes\tale\layouts\index.html</strong> page to <strong>.\layouts\index.html</strong>. </p>
<p>Once there, replace: <code>{{range (.Paginate .Site.RegularPages).Pages}}</code> with <code>{{ range where .Paginator.Pages "Section" "post" }}</code>. This will ensure only the "post" section will be displayed in the list.</p>
<p>I also wanted to add a brief footer, so go ahead and create a new file at <strong>.\layouts\footer.html</strong> and add the following code:</p>
<pre><code class="lang-html"><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> <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"{{ now }}"</span>&gt;</span>{{ now.Format "2006" }}<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span> {{ .Site.Author.name }}
    <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>
</code></pre>
<h3 id="heading-how-to-add-google-analytics">How to Add Google Analytics</h3>
<p>I also wanted to add some Google Analytics to my blog, and I noticed the theme didn't incorporate this functionality.  </p>
<p>Luckily, Hugo makes adding analytics extremely simple. Open up the <strong>config.toml</strong> file and add the following line:</p>
<pre><code class="lang-toml"><span class="hljs-attr">googleAnalytics</span> = <span class="hljs-string">""</span> <span class="hljs-comment"># The UA-XXX number from Google Analytics</span>
</code></pre>
<p>Once the configuration is saved, copy the <strong>.\themes\tale\layouts\partial\head.html</strong> file to <strong>.\layouts\partial\head.html</strong> and add the following code right below the <em>head</em> tag:</p>
<pre><code class="lang-go">{{ template <span class="hljs-string">"_internal/google_analytics_async.html"</span> . }}
</code></pre>
<p>There we go, now we have Google Analytics working. Cool!</p>
<h2 id="heading-how-to-write-content">How to Write Content</h2>
<p>Let's add a nice About page so people know everything there is about me. :)</p>
<pre><code class="lang-powershell">.\hugo.exe new about.md
</code></pre>
<p>To ensure that this page is added to the main menu, add the following line to the page's front matter: <code>menu: main</code>.</p>
<p><em>Note: To build Hugo, which will generate the content under the <strong>.\public</strong> folder, simply run <code>.\hugo.exe</code></em></p>
<h3 id="heading-what-is-front-matter">What is front matter?</h3>
<p>This was a new term for me. Essentially, front matter is just structured metadata for your content. </p>
<p>By default, your template will add the following metadata fields to each page or post you create:</p>
<ul>
<li>title</li>
<li>date</li>
<li>draft</li>
</ul>
<p>Other potentially useful front matter elements are:</p>
<ul>
<li>description - This allows you to enter a description for the content.</li>
<li>expiryDate - Set a datetime for when the content should no longer be published.</li>
<li>keywords - The meta keywords for the content.</li>
<li>lastmod - The datetime for when the content was last modified (if you are using the enableGitInfo configuration command, this will be automatically set as the last time the file was mofieid in Git)</li>
<li>markup - When set to "rst", you can use reStructuredText instead of Markdown (this feature is experimental)</li>
<li>publishDate - Set a date in the future for the content to be displayed.</li>
<li>slug - The tail of the output URL. Defaults to the filename if not specified.</li>
<li>summary - The text used when providing a summary of the article. I find this useful if I don't want the first paragraph to appear in the summary, which is the typical default.</li>
<li> - Use the plural form of the taxonomy index, such as <em>tags</em> or <em>categories</em>.</li>
</ul>
<h4 id="heading-how-to-create-an-archetype-for-blog-posts">How to create an archetype for blog posts</h4>
<p>Let's go ahead and change the default front matter we see for blog posts. In the archetypes folder, create a new file called <strong>posts.md</strong> and add the following:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ replace .Name "-" " " | title }}</span>"</span>
<span class="hljs-attr">date:</span> {{ <span class="hljs-string">.Date</span> }}
<span class="hljs-attr">draft:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">slug:</span> {{ <span class="hljs-string">.File.BaseFileName</span> }} <span class="hljs-comment"># Will take the filename as the slug. Feel free to change this to any format you like.  I like including this, so that I remind myself I have the option to change if I want.</span>

<span class="hljs-attr">summary:</span> <span class="hljs-string">""</span> <span class="hljs-comment"># Remove this if you want Hugo to just use the first 70 (configurable) characters of the post as the summary.</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">""</span>

<span class="hljs-comment"># Lists</span>
<span class="hljs-attr">keywords:</span>
<span class="hljs-attr">tags:</span>
<span class="hljs-attr">categories:</span>
<span class="hljs-meta">---</span>
</code></pre>
<p>Now let's do one final build with <code>.\hugo.exe</code> and get ready to configure our Git repository.</p>
<h2 id="heading-how-to-configure-git">How to Configure Git</h2>
<p>Time to configure the project for Git:</p>
<pre><code class="lang-powershell">git init
git remote add origin &lt;YOUR GIT URL&gt;
git push <span class="hljs-literal">-u</span> origin master
</code></pre>
<p>Great, we now have our code stored within our repository, and we are ready for deployment!</p>
<h2 id="heading-how-to-deploy-your-blog">How to Deploy Your Blog</h2>
<p>Now that we have our site stored in Git, it's time to deploy! Almost done - I'll now show how to deploy to Netlify in under 10 minutes.</p>
<p>First, we need to create the Netlify application (feel free to create an account using any method available):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-192.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, we need to create the site and tell it where our Git repository is for our Hugo content:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-193.png" alt="Image" width="600" height="400" loading="lazy">
<em>Tell Netlify where your site is located</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-194.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the repository</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-195.png" alt="Image" width="600" height="400" loading="lazy">
<em>These can be left at the default settings</em></p>
<p>Next up, we will set up a custom domain for our site:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-196.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the custom domain option</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-197.png" alt="Image" width="600" height="400" loading="lazy">
<em>Enter your custom domain</em></p>
<p>You should now see your domain with a message saying to <strong>Check DNS configuration</strong>. Click on that, and enter the provided DNS record information into whichever service provider manages your DNS records:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-198.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-200.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-201.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example configuration within my Cloudflare account</em></p>
<p>Once complete, wait a few minutes for the DNS settings to propagate, and then select <strong>Verify DNS configuration:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-202.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Behold, your site is now live!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-203.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example of my new blog hosted on Netlify!</em></p>
<p>Last, we should set up SSL for our site as a best practice. Netlify offers the option to use <a target="_blank" href="http://letsencrypt.org/howitworks/">Let's Encrypt</a> to automatically provision a certificate for your application. To do so, simply select <strong>Provision certificate:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-204.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Note: It can take quite some time for the certificate to be generated, so just be patient.</em></p>
<h3 id="heading-netlify-deployment-settings">Netlify deployment settings</h3>
<p>We have one final step before we are truly ready to use Netlify. Unfortunately, the version of Hugo used by Netlify is somewhat outdated by default. However, we can fix this by creating our own configuration for Netlify to follow when deploying our site.</p>
<p>First, create a file called <code>netlify.toml</code> in the root of your repository, and then add the following configuration:</p>
<pre><code class="lang-toml"><span class="hljs-section">[build]</span>
<span class="hljs-attr">publish</span> = <span class="hljs-string">"public"</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify"</span>

<span class="hljs-section">[context.production.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>
<span class="hljs-attr">HUGO_ENV</span> = <span class="hljs-string">"production"</span>
<span class="hljs-attr">HUGO_ENABLEGITINFO</span> = <span class="hljs-string">"true"</span>

<span class="hljs-section">[context.split1]</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify --enableGitInfo"</span>

<span class="hljs-section">[context.split1.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>
<span class="hljs-attr">HUGO_ENV</span> = <span class="hljs-string">"production"</span>

<span class="hljs-section">[context.deploy-preview]</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"</span>

<span class="hljs-section">[context.deploy-preview.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>

<span class="hljs-section">[context.branch-deploy]</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify -b $DEPLOY_PRIME_URL"</span>

<span class="hljs-section">[context.branch-deploy.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>

<span class="hljs-section">[context.next.environment]</span>
<span class="hljs-attr">HUGO_ENABLEGITINFO</span> = <span class="hljs-string">"true"</span>
</code></pre>
<p>All that's left to do is select <strong>Deploy site</strong> within the Netlify console, and your site is now live on a custom domain with SSL!</p>
<h2 id="heading-wrapup">Wrapup</h2>
<p>Whew! That was a lengthy blog post, but hopefully this shows how quick it is to get up and running with a "serverless" blog. Let's see what I learned :)</p>
<h3 id="heading-what-i-loved">What I loved</h3>
<ul>
<li>Super simple to build, just run <code>hugo serve</code></li>
<li>Live reload - make a change, save, and the page will reload</li>
<li>Just simple in general - I didn't need to deal with grunt, gulp, webpack, or others</li>
<li>Customizable output formats let you generate your static site, as well as a Google AMP site, JSON files, and so on.</li>
<li>FAST. Did I mention fast?</li>
<li>Can be deployed just about anywhere - whether using Netlify (my current choice), Amazon S3 &amp; Cloudfront, Heroku, GitHub Pages, and more.</li>
<li>Shortcodes are available if Markdown isn't enough</li>
<li>Continuous deployment - everything is version controlled, and deployed when I publish to the master branch</li>
<li>Allow commenting and sharing of posts</li>
</ul>
<h3 id="heading-challenges">Challenges</h3>
<ul>
<li>Hugo is sometimes too simple.  No plugins or extensions at all, etc.</li>
<li>Using Go is less intuitive and the shortcode feels messier than something like Vue</li>
<li>Not too many themes available, but I expect the library to keep growing, as there is a very active user base</li>
</ul>
<h3 id="heading-so-do-i-need-a-cms">So do I need a CMS?</h3>
<p>After all of this, I still had this question in the back of my mind.  And the answer is, "it depends".  </p>
<p>If I were to incorporate a lot of media, such as images or videos that I need to upload, it would certainly get tedious adding and organizing them all to the images folder in <strong>static</strong>. </p>
<p>At that point, I would look into a headless CMS such as Ghost, Netlify, or Sanity to manage the content, as long as I could still write my posts using Markdown.</p>
<h3 id="heading-references">References</h3>
<ul>
<li>https://medium.com/backticks-tildes/hugo101-getting-started-with-hugo-and-deploying-to-netlify-9a813fe23b94</li>
<li>https://blog.risingstack.com/static-site-generator-hugo-netlify/</li>
<li>http://cloudywithachanceofdevops.com/posts/2018/05/17/setting-up-google-analytics-on-hugo/</li>
<li>https://www.sitepoint.com/premium/books/a-beginner-s-guide-to-creating-a-static-website-with-hugo/read/1</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a Front End Application with Netlify ]]>
                </title>
                <description>
                    <![CDATA[ Hi everyone! In this article, I'm going to discuss how to deploy an application you've built. The application deployment process might seem complicated, and this might prevent some developers from deploying their applications after they've developed ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-your-front-end-app/</link>
                <guid isPermaLink="false">66b905d7472b70138041a587</guid>
                
                    <category>
                        <![CDATA[ app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Idris Olubisi ]]>
                </dc:creator>
                <pubDate>Sat, 09 Jan 2021 00:23:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/01/Turquoise-Confetti-Birthday-Greetings-Facebook-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hi everyone! In this article, I'm going to discuss how to deploy an application you've built.</p>
<p>The application deployment process might seem complicated, and this might prevent some developers from deploying their applications after they've developed them. </p>
<p>So here, I will be taking you through a seamless process to spin up your application which can then be accessed anywhere in the world via a URL.</p>
<h3 id="heading-table-of-contents">Table of contents</h3>
<ul>
<li>Why do you need to deploy your frontend applications?</li>
<li>What is Netlify?</li>
<li>What you can do with Netlify</li>
<li>How to deploy your site</li>
<li>Resources</li>
</ul>
<h2 id="heading-why-do-you-need-to-deploy-your-frontend-applications">Why do you need to deploy your frontend applications?</h2>
<p>There are numerous advantages to deploying your applications. Of course, you don't want your beautiful application to sit on your localhost forever. </p>
<p>Deploying your application makes it easier to share your project, side-gig, or startup with potential investors or future employers. If they can see those projects, it helps them gauge your skills. It also lets you show off your progress to the world.</p>
<p>In this article, we will be using the amazing [Netlify](https://www.netlify.com 'Netlify Homepage') platform to deploy our application.</p>
<p>That name sounds familiar, right? But if you haven't used it to deploy a web application yet, trust me I know how you feel. </p>
<p>I will take you through the steps to get your site deployed to <a target="_blank" href="https://www.netlify.com">Netlify</a> in less than 4 minutes. We'll also see some other functionalities that can be done with Netlify out of the box.</p>
<h2 id="heading-what-is-netlify">What is Netlify?</h2>
<p><a target="_blank" href="https://netlify.com/">Netlify</a> is a platform that lets developers automate modern web projects, and it's a place where you can deploy your application without worrying about frustrating configurations. </p>
<p>You can also integrate cool features and dynamic functionality like serverless functions and form handling on Netlify. Sounds good, right?</p>
<h2 id="heading-netlify-features">Netlify Features</h2>
<h3 id="heading-configure-builds">Configure builds</h3>
<p>Netlify helps you run the build command each time you push an update to your repository. </p>
<p>There are additional settings that you can configure like auto-deploy along with other useful deployment settings.</p>
<h3 id="heading-site-deploys-atomic-deployshttpsdocsnetlifycomsite-deploysoverview">Site deploys <a target="_blank" href="https://docs.netlify.com/site-deploys/overview/">(Atomic deploys)</a></h3>
<p>One of the awesome features Netlify has is site deployment. It ensures that your site is deployed and always consistent. </p>
<p>You can also enable deploy notifications, run a test while Netlify compares the new deploy with the existing one, and then update only the files that have been changed.</p>
<h3 id="heading-monitor-sites-netlify-analyticshttpsdocsnetlifycommonitor-sitesanalytics">Monitor sites <a target="_blank" href="https://docs.netlify.com/monitor-sites/analytics/">(Netlify Analytics)</a></h3>
<p>Monitoring your site might become difficult if you don't have proper infrastructure in place. </p>
<p>You can easily monitor your site'sactivities on this platform where you can track each log on the team's build usage.</p>
<h4 id="heading-domains-amp-https-register-new-domainshttpsdocsnetlifycomdomains-httpsnetlify-dnsdomain-registration">Domains &amp; HTTPS <a target="_blank" href="https://docs.netlify.com/domains-https/netlify-dns/domain-registration">(Register new domains)</a></h4>
<p>In simple terms, a domain is the URL anyone types into the browser to visit your site. You can assign a custom domain if you have already purchased one or secure a domain from Netlify. </p>
<p>Either way, the domain name system management is handled by Netlify. They also provide free automatic HTTPS on all sites. Cool right?</p>
<h4 id="heading-routing-learn-about-routinghttpsdocsnetlifycomroutingredirects">Routing <a target="_blank" href="https://docs.netlify.com/routing/redirects/">(Learn about routing)</a></h4>
<p>Routing, Redirects, proxies, and so on all become much easier when your site is deployed on Netlify.</p>
<h3 id="heading-visitor-access">Visitor access</h3>
<p>Here's another cool feature I enjoy: whenever you need to add someone to the team, you can set up role-based access controls that allow the Admin/Senior developer to take control and give access to individuals on the team to avoid escalations.</p>
<h3 id="heading-forms-netlify-formshttpsdocsnetlifycomformssetup">Forms <a target="_blank" href="https://docs.netlify.com/forms/setup/">(Netlify Forms)</a></h3>
<p>When you need to collect data from users on a site deployed on Netlify, you can do so using Netlify forms. This doesn't add API calls or extra JavaScript on your site, either.</p>
<p>Build bots handle form submission by parsing your HTML files directly at deploy time. You can also configure the receiver, group, and notifications.</p>
<h3 id="heading-functions-deploy-serverless-functionshttpsdocsnetlifycomfunctionsoverview">Functions <a target="_blank" href="https://docs.netlify.com/functions/overview/">(Deploy serverless functions)</a></h3>
<p>Serverless functions can be referred to as single-purpose, programmatic functions that are hosted on managed infrastructure.</p>
<p>Netlify lets you deploy serverless Lambda functions with management handled directly within Netlify, while they are built and deployed with the rest of your sites.</p>
<h3 id="heading-the-netlify-cli-netlify-command-line-interfacehttpsdocsnetlifycomcliget-started">The Netlify CLI <a target="_blank" href="https://docs.netlify.com/cli/get-started">(Netlify command-line interface)</a></h3>
<p>You might be wondering if all activities are carried out on the Netlify UI alone - well, no they're not. </p>
<p>There is another great feature that allows developers to deploy sites or do some configuration right from their terminal. The Netlify CLI can be used to run a local development server that can be shared, including plugins.</p>
<h3 id="heading-the-netlify-api-netlify-apihttpsdocsnetlifycomapiget-startedauthentication">The Netlify API <a target="_blank" href="https://docs.netlify.com/api/get-started/#authentication">(Netlify API)</a></h3>
<p>Netlify's API can be used to handle the deployment of sites, script injections, and more. It uses JSON for serialization, which conforms to the REST standard.</p>
<h3 id="heading-accounts-amp-billing">Accounts &amp; billing</h3>
<p>Learn about <a target="_blank" href="https://docs.netlify.com/accounts-and-billing/team-management/manage-team-members">managing team members</a> and how to transfer sites between teams.</p>
<blockquote>
<p>I hope you can now see how powerful Netlify is. Sut seeing sometimes can be deceiving, so let's try it out on our own.</p>
</blockquote>
<p>As you can tell from the title of this article, I will only be showing you how to deploy your site to netlify.com. But to explore other functionalities <a target="_blank" href="https://docs.netlify.com/">click here to read more</a>, practice, and explore.</p>
<h2 id="heading-how-to-deploy-a-site-to-netlify">How to Deploy a Site to Netlify</h2>
<h3 id="heading-step-one">Step One</h3>
<p>Login or signup on netlify.com if you are a new user. It's free :)</p>
<h3 id="heading-step-two">Step Two</h3>
<p>As shown below, all you need is to select a site from Git by clicking on the button with the name “New site from Git”.</p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119982/netlify%20deploy%20post/Screenshot.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-three">Step Three</h3>
<p>You will see the interface below where you can choose the Git provider where your site source code is hosted.</p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119980/netlify%20deploy%20post/select_git.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-four">Step Four</h3>
<p>Choose the repository you want to link to your site on Netlify.</p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119980/netlify%20deploy%20post/select_repo.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-five">Step Five</h3>
<p>We are almost there :)</p>
<p>This section allows you to get more control over how Netlify builds and deploys your site with the settings option shown below:</p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119981/netlify%20deploy%20post/select_to_deploy_site.jpg" alt="Netlify" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-six">Step Six</h3>
<p>Wait, Netlify is getting things ready for you. :)</p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119980/netlify%20deploy%20post/site_deploy_in_pgrogress.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-seven">Step Seven</h3>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119980/netlify%20deploy%20post/site.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<p>Congratulation Your site is Live!</p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119980/netlify%20deploy%20post/Screenshot1.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<p>Click on the generated URL with the .netlify.com extension below the header that states “Deploys for”.</p>
<p><strong>Lastly: You can also set up a new domain or change the generated one to something nice by clicking on the “…” that embeds “Edit site name” but it will end with .netlify.com. <a target="_blank" href="https://docs.netlify.com/domains-https/custom-domains/">Click here to read more</a></strong></p>
<p><img src="https://res.cloudinary.com/olanetsoft/image/upload/v1608119981/netlify%20deploy%20post/domain_setup.png" alt="Netlify" width="600" height="400" loading="lazy"></p>
<p>I hope you have found this guide useful :)</p>
<p>NOTE: the Netlify URL extension is now netlify.app. All netlify.com URLs will now be redirected to netlify.app.</p>
<p><strong>Please don't forget to check out my other articles, it gives me joy :) and the vibes to write more stuff.</strong></p>
<p>You can also reach out to me on <a target="_blank" href="https://twitter.com/olanetsoft">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Blog with Gatsby and Netlify CMS – A Complete Guide ]]>
                </title>
                <description>
                    <![CDATA[ By Mohammed Asker In this article, we are going to build a blog with Gatsby and Netlify CMS. You will learn how to install Gatsby on your computer and use it to quickly develop a super fast blog site. You are also going to learn how to add Netlify CM... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-blog-with-gatsby-and-netlify-cms/</link>
                <guid isPermaLink="false">66d46040d14641365a050921</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 06 Oct 2020 15:16:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c984e740569d1a4ca1946.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mohammed Asker</p>
<p>In this article, we are going to build a blog with Gatsby and Netlify CMS. You will learn how to install Gatsby on your computer and use it to quickly develop a super fast blog site.</p>
<p>You are also going to learn how to add Netlify CMS to your site by creating and configuring files, then connecting the CMS to your site through user authentication. </p>
<p>And finally, you'll learn how to access the CMS admin so that you can write your first blog post.</p>
<p>The complete code for this project can be found <a target="_blank" href="https://github.com/mohammedasker/foodblog">here</a>.</p>
<p>Here's a brief introduction to these tools.</p>
<h2 id="heading-what-is-gatsby">What is Gatsby?</h2>
<p><a target="_blank" href="https://www.gatsbyjs.com/">Gatsby</a> is a free and open-source framework based on React that helps you build fast websites and web apps. It is also a static site generator like Next.js, Hugo, and Jekyll.</p>
<p>It includes SEO (Search Engine Optimization), accessibility, and performance optimization from the get-go. This means that it will take you less time to build production-ready web apps than if you were building with React alone.</p>
<h2 id="heading-what-is-netlify-cms">What is Netlify CMS?</h2>
<p><a target="_blank" href="https://www.netlifycms.org/">Netlify CMS</a> is a CMS (Content Management System) for static site generators. It is built by the same people who made <a target="_blank" href="https://www.netlify.com/">Netlify</a>. It allows you to create and edit content as if it was WordPress, but it's a much simpler and user-friendly interface.</p>
<p>The main benefit of Netlify CMS is you don't have to create markdown files every time you want to write a post. This is useful for content writers who don't want to deal with code, text editors, repositories, and anything to do with tech - they can just focus on writing articles.</p>
<p>Alright, without any further ado, let's start building the blog!</p>
<p><strong>But before we get going, a quick heads up</strong>: This guide requires prior knowledge of JavaScript and React. If you are not comfortable with these tools yet, I've linked the resources at the end of the article to help you brush up on those skills. </p>
<p>Even if you're new to those technologies, I tried to make this guide as simple as I was able so you can follow along.</p>
<h2 id="heading-how-to-set-up-the-environment">How to set up the environment</h2>
<p>Before we can build Gatsby sites, we have to make sure that we have installed all the right software required for the blog.</p>
<h3 id="heading-install-nodejs">Install Node.js</h3>
<p>Node.js is an environment that can run JavaScript code outside of a web browser.</p>
<p>It is a tool that allows you to write backend server code instead of using other programming languages such as Python, Java, or PHP. Gatsby is built with Node.js and that's why we need to install it on our computer.</p>
<p>To install Node.js, go to the <a target="_blank" href="https://nodejs.org/en/download/">download page</a> and download it based on your operating system. </p>
<p>When you are done following the installation prompts, open the terminal and run <code>node -v</code> to check if it was installed correctly. Currently, the version should be 12.18.4 and above.</p>
<h3 id="heading-install-git">Install Git</h3>
<p>Git is a free and open-source distributed version control system that helps you manage your coding projects efficiently. </p>
<p>Gatsby starter uses Git to download and install its required files and that's why you need to have Git on your computer.</p>
<p>To install Git, follow the instructions based on your operating system:</p>
<ul>
<li><a target="_blank" href="https://www.atlassian.com/git/tutorials/install-git#mac-os-x">Install Git on Mac OS</a></li>
<li><a target="_blank" href="https://www.atlassian.com/git/tutorials/install-git#windows">Install Git on Windows</a></li>
<li><a target="_blank" href="https://www.atlassian.com/git/tutorials/install-git#linux">Install Git on Linux</a></li>
</ul>
<h3 id="heading-install-gatsby-cli">Install Gatsby CLI</h3>
<p>Gatsby CLI (Command Line Interface) is the tool that lets you build Gatsby-powered sites. By running this command, we can install any Gatsby sites and the plugins we want.</p>
<p>To install Gatsby CLI, open the terminal and run this command:</p>
<pre><code>npm install -g gatsby-cli
</code></pre><p>Once everything is set up successfully then we are ready to build our first Gatsby site.</p>
<h2 id="heading-how-to-build-a-gatsby-site">How to build a Gatsby site</h2>
<p>In this guide, we're going to use the default Gatsby starter theme, but you're free to choose any themes on the <a target="_blank" href="https://www.gatsbyjs.com/starters/?v=2">Gatsby starter library</a>. I personally use the <a target="_blank" href="https://github.com/LekoArts/gatsby-starter-minimal-blog">Lekoart theme</a> because the design is minimalist and beautiful, and it has a dark mode.</p>
<p>In the terminal, run this command to install the new Gatsby blog:</p>
<pre><code>gatsby <span class="hljs-keyword">new</span> foodblog https:<span class="hljs-comment">//github.com/gatsbyjs/gatsby-starter-blog</span>
</code></pre><p><strong>Note for Windows users</strong>: If you encounter "Error: Command failed with exit code 1: yarnpkg" while creating Gatsby site, see <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/26804">this page</a> to troubleshoot it. You may have to clean up dependencies of old yarn installations or follow the Gatsby on Windows instructions.</p>
<p>What's does this command line mean exactly? Let me explain.</p>
<ul>
<li><strong>new</strong> - This is the command line that creates a new Gatsby project</li>
<li><strong>foodblog</strong> - This is the name of the project. You can name it whatever you want here. I named this project <em>foodblog</em> as an example only.</li>
<li><strong>The URL</strong> (<a target="_blank" href="https://github.com/gatsbyjs/gatsby-starter-blog">https://github.com/gatsbyjs/gatsby-starter-blog</a>) - This URL specified points to a code repository that holds the starter code you want to use. In other words, I picked the theme for the project.</li>
</ul>
<p>Once the installation is complete, we'll run the <code>cd foodblog</code> command which will take us to the location of our project file.</p>
<pre><code>cd foodblog
</code></pre><p>Then we'll run <code>gatsby develop</code> that will start running on the local machine. Depending on the specs of your computer, it will take a little while before it is fully started.</p>
<pre><code>gatsby develop
</code></pre><p>Open a new tab in your browser and go to <code>http://localhost:8000/</code>. You should now see your new Gatsby site!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--33-.png" alt="Image" width="600" height="400" loading="lazy">
<em>How a Gatsby starter blog homepage looks</em></p>
<p>Now that we've created the blog, the next step is to add Netlify CMS to make writing blog posts easier.</p>
<h2 id="heading-how-to-add-netlify-cms-to-your-site">How to add Netlify CMS to your site</h2>
<p>Adding Netlify CMS to your Gatsby site involves 4 major steps:</p>
<ul>
<li>app file structure, </li>
<li>configuration, </li>
<li>authentication, and </li>
<li>accessing the CMS.</li>
</ul>
<p>Let's tackle each of these stages one at a time.</p>
<h3 id="heading-how-to-set-up-the-apps-file-structure">How to set up the app's file structure</h3>
<p>This section deals with the file structure of your project. We are going to create files that will contain all Netlify CMS codes.</p>
<p>When you open your text editor, you will see a lot of files. You can read <a target="_blank" href="https://github.com/gatsbyjs/gatsby-starter-blog#-whats-inside">this article</a> if you are curious about what each of these files does.</p>
<pre><code>├── node_modules
├── src
├── <span class="hljs-keyword">static</span>
├── .gitignore
├── .prettierrc
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── gatsby-ssr.js
├── LICENSE
├── package-lock.json
├── package.json
└── README.md
</code></pre><p>Do not worry about all these files — we are going to use very few of them here. </p>
<p>What we are looking for is the <code>static</code> folder. This is the folder where it will form the main structure of the Netlify CMS. </p>
<p>If your project does not have <code>Static</code> folder, then create the folder at the root directory of your project.</p>
<p>Inside the <code>static</code> folder, create an <code>admin</code> folder. Inside this folder, create two files <code>index.html</code> and <code>config.yml</code>:</p>
<pre><code>admin
 ├ index.html
 └ config.yml
</code></pre><p>The first file, <code>index.html</code>, is the entry point to your CMS admin. This is where Netlify CMS lives. You don't need to do styling or anything as it is already done for you with the script tag in the example below:</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">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">title</span>&gt;</span>Content Manager<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">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.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>The second file, <code>config.yml</code>, is the main core of the Netlify CMS. It's going to be a bit complicated as we are going to write backend code. We'll talk more about it in the configuration section.</p>
<h3 id="heading-how-to-configure-the-back-end">How to configure the back end</h3>
<p>In this guide, we are using Netlify for hosting and authentication and so the backend configuration process should be relatively straightforward. Add all the code snippets in this section to your <code>admin/config.yml</code> file.</p>
<p>We'll begin by adding the following codes:</p>
<pre><code class="lang-yml"><span class="hljs-attr">backend:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">git-gateway</span>
  <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>
</code></pre>
<p><strong>Heads up</strong>: This code above works for GitHub and GitLab repositories. If you're using Bitbucket to host your repository, follow these <a target="_blank" href="https://www.netlifycms.org/docs/bitbucket-backend/">instructions</a> instead.</p>
<p>The code we just wrote specifies your backend protocol and your publication branch (which is branch: master). Git Gateway is an open-source API that acts as a proxy between authenticated users of your site and your site repository. I'll explain more what this does in the authentication section.</p>
<p>Next up, we will write <code>media_folder: "images/uploads"</code>. This will allow you to add media files like photos directly to your CMS. Then you won't need to use a text editor to manually add media and all that.</p>
<pre><code class="lang-yml"><span class="hljs-attr">media_folder:</span> <span class="hljs-string">"images/uploads"</span>
</code></pre>
<p>Make sure you created a folder called <code>images</code> in the <code>admin</code> folder. Inside the <code>images</code> folder, create an <code>uploads</code> folder as this is the place where you'll host your images.</p>
<h3 id="heading-configure-collections">Configure Collections</h3>
<p>The collections will define the structure for the different content types on your static site. As every site can be different, how you configure the collection's settings will differ from one site to another.</p>
<p>Let's just say your site has a blog, with the posts stored in <code>content/blog</code>, and files saved in a date-title format, like <code>2020-09-26-how-to-make-sandwiches-like-a-pro.md</code>. Each post begins with settings in the YAML-formatted front matter in this way:</p>
<pre><code class="lang-md">---
layout: blog
title: "How to make sandwiches like a pro"
date: 2020-09-26 11:59:59
<span class="hljs-section">thumbnail: "/images/sandwich.jpg"
---</span>

This is the post body where I write about how to make a sandwich so good that will impress Gordon Ramsay.
</code></pre>
<p>With this example above, this is how you will add <code>collections</code> settings to your Netlify CMS <code>config.yml</code> file:</p>
<pre><code class="lang-yml"><span class="hljs-attr">collections:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"blog"</span>
    <span class="hljs-attr">label:</span> <span class="hljs-string">"Blog"</span>
    <span class="hljs-attr">folder:</span> <span class="hljs-string">"content/blog"</span>
    <span class="hljs-attr">create:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">slug:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{year}}</span>-<span class="hljs-template-variable">{{month}}</span>-<span class="hljs-template-variable">{{day}}</span>-<span class="hljs-template-variable">{{slug}}</span>"</span>
    <span class="hljs-attr">fields:</span>
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Layout"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"layout"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"hidden"</span>, <span class="hljs-attr">default:</span> <span class="hljs-string">"blog"</span>}
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Title"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"title"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"string"</span>}
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Publish Date"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"date"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"datetime"</span>}
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Body"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"body"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"markdown"</span>}
</code></pre>
<p>Let's examine what each of these fields does:</p>
<ul>
<li><code>name</code>:  This one is used in routes like /admin/collections/blog</li>
<li><code>label</code>: This one is used in the UI (User Interface). When you are in the admin page, you will see a big word "Blog" on the top of the screen. That big word "Blog" is the label.</li>
<li><code>folder</code>: This one points to the file path where your blog posts are stored.</li>
<li><code>create</code>: This one lets the user (you or whoever has admin access) create new documents (blog posts in this case) in these collections.</li>
<li><code>slug</code>: This one is the template for filenames. <code>{{year}}</code>, <code>{{month}}</code>, and <code>{{day}}</code> which are pulled from the post's date field or save date. <code>{{slug}}</code> is a URL-safe version of the post's title. By default it is <code>{{slug}}</code>.</li>
</ul>
<p>The fields are where you can customize the content editor (the page where you write the blog post). You can add stuff like ratings (1-5), featured images, meta descriptions, and so on.</p>
<p>For instance, in this particular code, we add curly braces <code>{}</code>. Inside them we write <code>label</code> with the value "Publish Date" which will be the label in the editor UI. </p>
<p>The <code>name</code> field is the name of the field in the front matter and we name it "date" since the purpose of this field is to enter the date input.</p>
<p>And lastly, the widget determines how the UI style will look and the type of data we can enter. In this case, we wrote <code>"datetime"</code> which means we can only enter the date and time.</p>
<pre><code class="lang-yml"><span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Publish Date"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"date"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"datetime"</span>}
</code></pre>
<p>You can check the list right <a target="_blank" href="https://www.netlifycms.org/docs/widgets/">here</a> to see what exactly you can add. If you want, you can even create your own widgets, too. For the sake of brevity, we'll try to keep things simple here.</p>
<h3 id="heading-enable-authentication">Enable Authentication</h3>
<p>At this point, we are nearly done with the installation and configuration of Netlify CMS. Now it's time to connect your Gatsby site to the CMS by enabling authentication. </p>
<p>We'll add some HTML code and then activate some features from Netlify. After that, you are on the way to creating your first blog post.</p>
<p>We are going to need a way to connect a front end interface to the backend so that we can handle authentication. To do that, add this HTML script tag to two files:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://identity.netlify.com/v1/netlify-identity-widget.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>The first file to add this script tag is the <code>admin/index.html</code> file. Place it between the <code>&lt;head&gt;</code> tags. And the second file to add the tag is the <code>public/index.html</code> file. This one also goes in between the <code>&lt;head&gt;</code> tags.</p>
<p>When a user logs in with the Netlify Identity widget, an access token directs them to the site homepage. In order to complete the login and get back to the CMS, redirect the user back to the <code>/admin/</code> path.</p>
<p>To do this, add the following code before the closing <code>body</code> tag of the <code>public/index.html</code> file:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.netlifyIdentity) {
    <span class="hljs-built_in">window</span>.netlifyIdentity.on(<span class="hljs-string">"init"</span>, <span class="hljs-function"><span class="hljs-params">user</span> =&gt;</span> {
      <span class="hljs-keyword">if</span> (!user) {
        <span class="hljs-built_in">window</span>.netlifyIdentity.on(<span class="hljs-string">"login"</span>, <span class="hljs-function">() =&gt;</span> {
          <span class="hljs-built_in">document</span>.location.href = <span class="hljs-string">"/admin/"</span>;
        });
      }
    });
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>With this, we are now done writing the code and it's time to visit Netlify to activate authentication.</p>
<p>Before we move on, you should Git commit your changes and push them to the repository. Plus, you will have to deploy your site live so you can access the features in the Enable Identity and Git Gateway section.</p>
<h2 id="heading-deploy-your-site-live-with-netlify">Deploy your site live with Netlify</h2>
<p>We are going to use Netlify to deploy our Gatsby site live. The deployment process is pretty straightforward, quick, and most importantly, it comes with a free SSL (Secure Sockets Layer). This means your site is protected (you can tell by looking at the green lock on the browser search).</p>
<p>If you haven't signed up for the platform, you can do it <a target="_blank" href="https://app.netlify.com/signup?_ga=2.69477016.986166254.1601369549-1254573554.1571849986">right here</a>. When you've finished signing up, you can begin the deployment process by following these 3 steps.</p>
<ol>
<li>Click the "New site from Git" button to create a new site to be deployed. Choose the Git provider where your site is hosted. My site is hosted on GitHub so that's what I will choose.</li>
<li>Choose the repository you want to connect to Netlify. The name of my Gatsby site is "foodblog" but you have to pick your own project name.</li>
<li>The last one asks how you would like Netlify to adjust your builds and deploy your site. We are going to leave everything as it is and we will click the "Deploy site" button. This will begin deploying your site to live.</li>
</ol>
<p>Once the deployment is complete, you can visit your live site by clicking the green link that has been generated for you on the top left of the screen. Example: <code>https://random_characters.netlify.app</code>.</p>
<p>With this, the world can now view your site. You can replace the weird URL with your custom domain by reading this <a target="_blank" href="https://docs.netlify.com/domains-https/custom-domains/#definitions">documentation</a>.</p>
<h3 id="heading-how-to-enable-identity-and-git-gateway">How to enable Identity and Git Gateway</h3>
<p>Netlify's Identity and Git Gateway services help you manage CMS admin users for your site without needing them to have an account with your Git host (Like GitHub) or commit access on your repository.</p>
<p>To activate these services, head to your site dashboard on Netlify and follow these steps:</p>
<ol>
<li>Go to <strong>Settings</strong> &gt; <strong>Identity</strong>, and select <strong>Enable Identity</strong> service.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--34-.png" alt="Image" width="600" height="400" loading="lazy">
<em>In the Overview page of your site, click the "Settings" link.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--36-.png" alt="Image" width="600" height="400" loading="lazy">
<em>After clicking "Settings", scroll down the left sidebar and click the "Identity" link.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--37-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Click the "Enable Identity" button to activate the Identity feature.</em></p>
<ol start="2">
<li>Under <strong>Registration</strong> preferences, select <strong>Open</strong> or <strong>Invite only</strong>. Most of the time, you want only invited users to access your CMS. But if you are just experimenting, you can leave it open for convenience.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--38-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Under the Identity submenu, click the "Registration" link and you'll be taken to the registration preferences.</em></p>
<ol start="3">
<li>Scroll down to <strong>Services</strong> &gt; <strong>Git Gateway</strong>, and click <strong>Enable Git Gateway</strong>. This authenticates with your Git host and generates an API access token. </li>
</ol>
<p>In this case, we're leaving the <strong>Roles</strong> field blank, which means any logged-in user may access the CMS.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--40-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Under the Identity submenu, click the "Services" link.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--41-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Click the "Enable Git Gateway" button to activate the Git Gateway feature.</em></p>
<p>With this, your Gatsby site has been connected with Netlify CMS. All that is left is to access the CMS admin and write blog posts.</p>
<h2 id="heading-how-to-access-the-cms">How to access the CMS</h2>
<p>All right, you are now ready to write your first blog post!</p>
<p>There are two ways to access your CMS admin, depending on what accessing options you chose from the Identity.</p>
<p>If you selected <strong>Invite only</strong>, you can invite yourself and other users by clicking the Invite user button. Then an email message will be sent with an invitation link to login to your CMS admin. Click the confirmation link and you'll be taken to the login page.</p>
<p>Alternatively, if you selected <strong>Open</strong>, you can access your site's CMS directly at <code>yoursite.com/admin/</code>. You will be prompted to create a new account. When you submit it, a confirmation link will be sent to your email. Click the confirmation link to complete the signup process and you'll be taken to the CMS page.</p>
<p><strong>Note</strong>: If you cannot access your CMS admin after clicking the link from the email, the solution is to copy the link in the browser starting with <code>#confirmation_token=random_characters</code> and paste the link after the hashtag "#", like this: <code>https://yoursite.com/admin/#confirmation_token=random_characters</code>. This should fix the problem.</p>
<p>If everything goes well, you should see your site's admin dashboard:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--42-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Netlify CMS admin.</em></p>
<p>You can create your new post by clicking the "New post" button.</p>
<p>When you're ready to publish your post, you can click the "Publish Now" button to publish it immediately.</p>
<p>When you hit the publish button, the post file is automatically created. Then it will add to the changes with the commit message based on the name of the post along with the date and time of publishing. Finally, it will be pushed to the host repository, and from there your post will be seen live.</p>
<p>You can view the changes by looking at the commit message in your host repository.</p>
<p>After waiting for a few minutes, your new post should be live.</p>
<h3 id="heading-one-more-thing">One more thing</h3>
<p>The last thing to do is clean up the sample articles. To delete these posts, go to the blog files in your text editor and delete them one by one. Make sure you check your terminal when deleting them so that there will be no issues on your site.</p>
<p>Once all the sample posts are cleared out, commit these changes and push them to the repository.</p>
<p>And now, you are all done! You can now create your new posts from the comfortable CMS dashboard and share your stories to the world.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this guide you have learned how to:</p>
<ul>
<li>Create a Gatsby blog site</li>
<li>Added the Netlify CMS to your Gatsby site by creating and configuring files</li>
<li>Enable user authentication by activating Identity and Git Gateway</li>
<li>Access your site's CMS admin</li>
<li>Publish your first post powered by Gatsby and Netlify CMS</li>
</ul>
<p>By the end of this guide, you should now be able to enjoy writing blog posts with a fast website and simple content editor. And you probably don't have to touch the code unless it needs further customization.</p>
<p>There is still more to cover about Gatsby and Netlify CMS. One of the best ways to learn about them is to go through their documentation.</p>
<p>I hope you found this guide beneficial, and happy posting!</p>
<p><a target="_blank" href="https://www.mohammedasker.com/">Check out my blog</a> to learn more tips, tricks, and tutorials about web development.</p>
<p>Cover photo by <a target="_blank" href="https://unsplash.com/@neonbrand?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">NeONBRAND</a> on <a target="_blank" href="https://unsplash.com/s/photos/blogging?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>.</p>
<h3 id="heading-resources-for-javascript-and-react">Resources for JavaScript and React</h3>
<p>Here are some resources that may help you to learn JavaScript and React:</p>
<p><strong>JavaScript</strong></p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">Official JavaScript Documentation</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/">freeCodeCamp</a></li>
<li><a target="_blank" href="https://www.w3schools.com/js/">W3Schools: JavaScript Tutorial</a></li>
<li><a target="_blank" href="https://javascript.info/">JavaScript.info</a></li>
</ul>
<p><strong>React</strong></p>
<ul>
<li><a target="_blank" href="https://reactjs.org/docs/getting-started.html">Official React Documentation</a></li>
<li><a target="_blank" href="https://www.udemy.com/course/complete-react-developer-zero-to-mastery/">Complete React Developer in 2020 (w/ Redux, Hooks, GraphQL)</a></li>
<li><a target="_blank" href="https://scrimba.com/learn/learnreact">Scrimba: Learn React for free</a></li>
<li><a target="_blank" href="https://flaviocopes.com/page/react-handbook/">Flavio Copes: The React Handbook</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
