<?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[ social media - 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[ social media - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 23 Jun 2026 22:44:50 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/social-media/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build an AI Social Media Post Scheduler Using Gemini and Late API in Next.js ]]>
                </title>
                <description>
                    <![CDATA[ Social media has become a vital tool for people and businesses to share ideas, promote products, and connect with their target audience. But creating posts regularly and managing schedules across multiple platforms can be time-consuming and repetitiv... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-ai-social-media-post-scheduler-using-gemini-and-late-api-in-nextjs/</link>
                <guid isPermaLink="false">697cebbfa59b6f85b8ca1c5f</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #ai-tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Asaolu ]]>
                </dc:creator>
                <pubDate>Fri, 30 Jan 2026 17:34:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769794478505/be596e27-4c88-45b3-8547-f715c82e0eda.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Social media has become a vital tool for people and businesses to share ideas, promote products, and connect with their target audience. But creating posts regularly and managing schedules across multiple platforms can be time-consuming and repetitive.</p>
<p>In this tutorial, you’ll learn how to build an AI-powered social media post scheduler using <a target="_blank" href="https://ai.google.dev/gemini-api/docs/quickstart"><strong>Gemini</strong></a><strong>,</strong> <a target="_blank" href="https://docs.getlate.dev/">Late API</a>, and <a target="_blank" href="https://nextjs.org/">Next.js</a>.</p>
<p>We’ll use the Gemini API to generate engaging social media content from user prompts, Next.js to handle both the frontend and backend of the application, and Late API to publish and schedule posts across multiple social media platforms from a single platform.</p>
<p><img src="https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExd3hreWI5cGw0MnRxenc5NGJqdHczYjdmNjh2Zmxrb2c3ZXo2empzYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/azjux6zMBW3cWxK21P/giphy.gif" alt="Social media platforms" class="image--center mx-auto" width="340" height="340" loading="lazy"></p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setup-and-installation">Setup and Installation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-schedule-social-media-posts-with-late">How to Schedule Social Media Posts with Late</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-nextjs-app-interface">How to Build the Next.js App Interface</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-gemini-api-for-post-generation">How to integrate Gemini API for Post Generation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-late-api-in-nextjs">How to Use Late API in Next.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To fully understand this tutorial, you need to have a basic understanding of React or Next.js.</p>
<p>We will use the following tools:</p>
<ul>
<li><p><a target="_blank" href="https://getlate.dev/"><strong>Late API</strong></a>: A social media API that lets you create and schedule posts across 13 social media platforms from a single dashboard.</p>
</li>
<li><p><a target="_blank" href="https://nextjs.org/"><strong>Next.js</strong></a>: A React framework for building fast, scalable web applications, handling both the frontend and backend.</p>
</li>
<li><p><a target="_blank" href="https://ai.google.dev/gemini-api/docs/quickstart"><strong>Google Gemini API</strong></a>: Provides access to Google’s AI models for generating text and other content based on user prompts.</p>
</li>
</ul>
<h2 id="heading-setup-and-installation">Setup and Installation</h2>
<p>Create a new Next.js project using the following code snippet:</p>
<pre><code class="lang-bash">npx create-next-app post-scheduler
</code></pre>
<p>Install the project dependencies. We’ll use <a target="_blank" href="https://github.com/iamkun/dayjs"><strong>Day.js</strong></a> to work with JavaScript dates, making it easier to schedule and publish social media posts at the correct time.</p>
<pre><code class="lang-bash">npm install @google/genai dayjs utc
</code></pre>
<p>Next, add a <code>.env.local</code> file containing your Gemini API key at the root of your Next.js project:</p>
<pre><code class="lang-bash">GEMINI_API_KEY=&lt;paste_your API key&gt;
</code></pre>
<p>Once everything is set up, your Next.js project is ready. Now, let's start building! 🚀</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769613408264/abd09717-9403-4480-a319-a0d430b635a3.png" alt="Late API and the available social media platforms" class="image--center mx-auto" width="2496" height="1369" loading="lazy"></p>
<h2 id="heading-how-to-schedule-social-media-posts-with-late">How to Schedule Social Media Posts with Late</h2>
<p><strong>Late</strong> is an all-in-one social media scheduling platform that allows you to connect your social media accounts and publish posts across multiple platforms. In this section, you’ll learn how to create and schedule social media posts using the Late dashboard.</p>
<p>To get started, create a <a target="_blank" href="https://getlate.dev/"><strong>Late account</strong></a> and sign in.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769613666616/61074a32-5fa3-4c14-8e96-dfc8a86fec0f.png" alt="Sign in and get Late API key" class="image--center mx-auto" width="2503" height="1247" loading="lazy"></p>
<p>Create an API key and add it to the <code>.env.local</code> file within your Next.js project.</p>
<pre><code class="lang-bash">LATE_API_KEY=&lt;your_API_key&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769613764201/5dbd7d92-0fa8-46d3-9bf0-a145ef4fe65e.png" alt="Copy Late API key" class="image--center mx-auto" width="2536" height="1402" loading="lazy"></p>
<p>Connect your social media accounts to Late so you can manage and publish posts across all platforms.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769613835179/fa6ec3df-4cca-4fc9-bfac-3b5f2815dfdd.png" alt="Social media platforms" class="image--center mx-auto" width="2455" height="1249" loading="lazy"></p>
<p>After connecting your social media accounts via OAuth, you can start writing, posting, and scheduling content directly to your social media platforms.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769613994931/d9862aa7-2a57-4373-afe0-8513eea6bffd.png" alt="Twitter (X) account connected" class="image--center mx-auto" width="2559" height="1425" loading="lazy"></p>
<p>Late lets you write your post content and attach media files directly from the dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769614064645/0eea26fe-6d51-4ae3-8b33-d96cc539e88d.png" alt="Create Social media contents from your dashboard" class="image--center mx-auto" width="2559" height="1419" loading="lazy"></p>
<p>You can choose when your content should be published: post immediately, schedule for later, add it to a job queue, or save it as a draft.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769614116012/c241c2ed-37d3-4973-8d62-0e288e97f561.png" alt="Publish your post" class="image--center mx-auto" width="2536" height="1423" loading="lazy"></p>
<p>Once a post is published, you can view its status and preview it directly in the dashboard using the post link.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769614187249/574ec680-7aad-4701-aa7f-9db4aebb146a.png" alt="Social media post created with Late" class="image--center mx-auto" width="2538" height="1427" loading="lazy"></p>
<p>🎉 <strong>Congratulations!</strong> You’ve successfully created your first post using the Late dashboard. In the next sections, you’ll learn how to use the <a target="_blank" href="https://docs.getlate.dev/core/posts">Late API</a> to create and schedule posts directly from your applications.</p>
<h2 id="heading-how-to-build-the-nextjs-app-interface">How to Build the Next.js App Interface</h2>
<p>In this section, you’ll build the user interface for the application. The app uses a single-page route with conditional rendering to display recent posts, an AI prompt input field, and a form that allows users to create or schedule posts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769614332014/a6348263-bdba-4100-a63e-0698ee7a9ed4.gif" alt="App Overview" class="image--center mx-auto" width="800" height="443" loading="lazy"></p>
<p>Before we proceed, create a <code>types.d.ts</code> file within your Next.js project and copy the following code snippet into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Post {
    _id: <span class="hljs-built_in">string</span>;
    content: <span class="hljs-built_in">string</span>;
    scheduledFor: <span class="hljs-built_in">string</span>;
    status: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> AIFormProps {
    handleGeneratePost: <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent&lt;HTMLFormElement&gt;</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
    useAI: <span class="hljs-built_in">boolean</span>;
    setUseAI: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">boolean</span>&gt;&gt;;
    prompt: <span class="hljs-built_in">string</span>;
    setPrompt: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
    disableBtn: <span class="hljs-built_in">boolean</span>;
}

<span class="hljs-keyword">interface</span> FormProps {
    handlePostSubmit: <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent&lt;HTMLFormElement&gt;</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
    content: <span class="hljs-built_in">string</span>;
    setContent: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
    date: <span class="hljs-built_in">string</span>;
    setDate: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
    disableBtn: <span class="hljs-built_in">boolean</span>;
    setUseAI: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">boolean</span>&gt;&gt;;
    useAI: <span class="hljs-built_in">boolean</span>;
}
</code></pre>
<p>The <code>types.d.ts</code> file defines all the data structures and type declarations used throughout the application.</p>
<p>Copy the following code snippet into the <code>app/page.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> Nav <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Nav"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> NewPost <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/NewPost"</span>;
<span class="hljs-keyword">import</span> PostsQueue <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/PostsQueue"</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">Page</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [showPostQueue, setShowPostQueue] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">'w-full h-screen'</span>&gt;
            &lt;Nav showPostQueue={showPostQueue} setShowPostQueue={setShowPostQueue} /&gt;
            {showPostQueue ? &lt;PostsQueue /&gt; : &lt;NewPost /&gt;}
        &lt;/div&gt;
    );
}
</code></pre>
<p>The <code>Page</code> component renders the <code>Nav</code> component and uses conditional rendering to display either the <code>PostsQueue</code> or <code>NewPost</code> component based on the value of the <code>showPostQueue</code> state.</p>
<p>Create a <code>components</code> folder to store the page components used in the application.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> app
mkdir components &amp;&amp; <span class="hljs-built_in">cd</span> components
touch Nav.tsx NewPost.tsx PostElement.tsx PostsQueue.tsx
</code></pre>
<p>Add the code snippet below to the <code>Nav.tsx</code> file:</p>
<pre><code class="lang-typescript"><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">Nav</span>(<span class="hljs-params">{
    showPostQueue,
    setShowPostQueue,
}: {
    showPostQueue: <span class="hljs-built_in">boolean</span>;
    setShowPostQueue: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">boolean</span>&gt;&gt;;
}</span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;nav&gt;
            &lt;h2&gt;Post Scheduler&lt;/h2&gt;

            &lt;button onClick={<span class="hljs-function">() =&gt;</span> setShowPostQueue(!showPostQueue)}&gt;
                {showPostQueue ? <span class="hljs-string">"New Post"</span> : <span class="hljs-string">"Schedule Queue"</span>}
            &lt;/button&gt;
        &lt;/nav&gt;
    );
}
</code></pre>
<p>Copy the following code snippet into the <code>PostsQueue.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useEffect, useState, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> PostElement <span class="hljs-keyword">from</span> <span class="hljs-string">"./PostElement"</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">PostsQueue</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [posts, setPosts] = useState&lt;Post[]&gt;([]);
    <span class="hljs-keyword">const</span> [loading, setLoading] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">'p-4'</span>&gt;
            &lt;h2 className=<span class="hljs-string">'text-xl font-bold'</span>&gt;Scheduled Posts&lt;/h2&gt;

            {loading ? (
                &lt;p className=<span class="hljs-string">'text-sm'</span>&gt;Loading scheduled posts...&lt;/p&gt;
            ) : (
                &lt;div className=<span class="hljs-string">'mt-4'</span>&gt;
                    {posts.length &gt; <span class="hljs-number">0</span> ? (
                        posts.map(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> &lt;PostElement key={post._id} post={post} /&gt;)
                    ) : (
                        &lt;p&gt;No scheduled posts available.&lt;/p&gt;
                    )}
                &lt;/div&gt;
            )}
        &lt;/div&gt;
    );
}
</code></pre>
<p>The <code>PostsQueue.tsx</code> component displays a list of previously created posts along with their current status, showing whether each post has been published or scheduled for a later time. While the data is being loaded, it shows a loading message, and once loaded, it renders each post using the <code>PostElement</code> component.</p>
<p>Add the following to the <code>PostElement.tsx</code> component:</p>
<pre><code class="lang-typescript"><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">PostElement</span>(<span class="hljs-params">{ post }: { post: Post }</span>) </span>{
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> formatReadableTime = <span class="hljs-function">(<span class="hljs-params">isoString: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(isoString); <span class="hljs-comment">// parses UTC automatically</span>
        <span class="hljs-keyword">return</span> date.toLocaleString(<span class="hljs-literal">undefined</span>, {
            year: <span class="hljs-string">"numeric"</span>,
            month: <span class="hljs-string">"short"</span>,
            day: <span class="hljs-string">"numeric"</span>,
            hour: <span class="hljs-string">"2-digit"</span>,
            minute: <span class="hljs-string">"2-digit"</span>,
            second: <span class="hljs-string">"2-digit"</span>,
            hour12: <span class="hljs-literal">true</span>, <span class="hljs-comment">// set to false for 24h format</span>
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">'p-4 border flex items-center justify-between  space-x-4 rounded mb-2 hover:bg-gray-100 cursor-pointer'</span>&gt;
            &lt;div&gt;
                &lt;p className=<span class="hljs-string">'font-semibold text-sm'</span>&gt;{post.content.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>)}&lt;/p&gt;
                &lt;p className=<span class="hljs-string">'text-blue-400 text-xs'</span>&gt;
                    Scheduled <span class="hljs-keyword">for</span>: {formatReadableTime(post.scheduledFor)}
                &lt;/p&gt;
            &lt;/div&gt;

            &lt;p className=<span class="hljs-string">'text-sm text-red-500'</span>&gt;{post.status}&lt;/p&gt;
        &lt;/div&gt;
    );
}
</code></pre>
<p>Finally, copy the following code snippet into the <code>NewPost.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useState } <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">NewPost</span>(<span class="hljs-params"></span>) </span>{
 <span class="hljs-keyword">const</span> [disableBtn, setDisableBtn] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
 <span class="hljs-keyword">const</span> [useAI, setUseAI] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
 <span class="hljs-keyword">const</span> [content, setContent] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);
 <span class="hljs-keyword">const</span> [prompt, setPrompt] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);
 <span class="hljs-keyword">const</span> [date, setDate] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);

 <span class="hljs-comment">//👇🏻 generates post content</span>
 <span class="hljs-keyword">const</span> handleGeneratePost = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
  e.preventDefault();
  setDisableBtn(<span class="hljs-literal">true</span>);
 };

 <span class="hljs-comment">//👇🏻 create/schedule post</span>
 <span class="hljs-keyword">const</span> handlePostSubmit = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
  e.preventDefault();
 };

 <span class="hljs-keyword">return</span> (
  &lt;div className=<span class="hljs-string">'w-full p-4  h-[90vh] flex flex-col items-center justify-center border-t'</span>&gt;
   &lt;h3 className=<span class="hljs-string">'text-xl font-bold'</span>&gt;New Post&lt;/h3&gt;

   {useAI ? (
    &lt;AIPromptForm
     handleGeneratePost={handleGeneratePost}
     useAI={useAI}
     setUseAI={setUseAI}
     prompt={prompt}
     setPrompt={setPrompt}
     disableBtn={disableBtn}
    /&gt;
   ) : (
    &lt;PostForm
     handlePostSubmit={handlePostSubmit}
     content={content}
     setContent={setContent}
     date={date}
     setDate={setDate}
     disableBtn={disableBtn}
     setUseAI={setUseAI}
     useAI={useAI}
    /&gt;
   )}
  &lt;/div&gt;
 );
}
</code></pre>
<p>The <code>NewPost</code> component conditionally renders the <code>AIPromptForm</code> and the <code>PostForm</code>. When a user chooses to generate content using AI, the AIPromptForm component is displayed to collect the prompt. Once the content is generated, the PostForm component is shown, allowing the user to edit, create, or schedule the post.</p>
<p>Add the components below inside the <code>NewPost.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AIPromptForm = <span class="hljs-function">(<span class="hljs-params">{
    handleGeneratePost,
    useAI,
    setUseAI,
    prompt,
    setPrompt,
    disableBtn,
}: AIFormProps</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        &lt;form onSubmit={handleGeneratePost}&gt;
            &lt;p onClick={<span class="hljs-function">() =&gt;</span> setUseAI(!useAI)}&gt;Exit AI &lt;/p&gt;
            &lt;textarea
                rows={<span class="hljs-number">3</span>}
                required
                value={prompt}
                onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setPrompt(e.target.value)}
                placeholder=<span class="hljs-string">'Enter prompt...'</span>
            /&gt;
            &lt;button <span class="hljs-keyword">type</span>=<span class="hljs-string">'submit'</span> disabled={disableBtn}&gt;
                {disableBtn ? <span class="hljs-string">"Generating..."</span> : <span class="hljs-string">"Generate Post with AI"</span>}
            &lt;/button&gt;
        &lt;/form&gt;
    );
};

<span class="hljs-comment">// 👇🏻 Post Form component</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PostForm = <span class="hljs-function">(<span class="hljs-params">{
    handlePostSubmit,
    content,
    setContent,
    date,
    setDate,
    disableBtn,
    setUseAI,
    useAI,
}: FormProps</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> getNowForDatetimeLocal = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> now = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(now.getTime() - now.getTimezoneOffset() * <span class="hljs-number">60000</span>)
            .toISOString()
            .slice(<span class="hljs-number">0</span>, <span class="hljs-number">16</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;form onSubmit={handlePostSubmit}&gt;
            &lt;p onClick={<span class="hljs-function">() =&gt;</span> setUseAI(!useAI)}&gt;Generate posts <span class="hljs-keyword">with</span> AI &lt;/p&gt;
            &lt;textarea
                value={content}
                onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setContent(e.target.value)}
                rows={<span class="hljs-number">4</span>}
                placeholder=<span class="hljs-string">"What's happening?"</span>
                required
                maxLength={<span class="hljs-number">280</span>}
            /&gt;
            &lt;input
                <span class="hljs-keyword">type</span>=<span class="hljs-string">'datetime-local'</span>
                min={getNowForDatetimeLocal()}
                value={date}
                onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setDate(e.target.value)}
            /&gt;
            &lt;button disabled={disableBtn} <span class="hljs-keyword">type</span>=<span class="hljs-string">'submit'</span>&gt;
                {disableBtn ? <span class="hljs-string">"Posting..."</span> : <span class="hljs-string">"Create post"</span>}
            &lt;/button&gt;
        &lt;/form&gt;
    );
};
</code></pre>
<p>Congratulations! You've completed the application interface.</p>
<h2 id="heading-how-to-integrate-gemini-api-for-post-generation">How to integrate Gemini API for Post Generation</h2>
<p>Here, you will learn how to generate post content from the user's prompt using the Gemini API.</p>
<p>Before we proceed, make sure you have copied your API key from the <a target="_blank" href="https://ai.google.dev/gemini-api/docs/api-key">Google AI Studio</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769615386615/ccbf6a95-fe63-412a-955f-b4fa5e4234fe.png" alt="Create Gemini API key" class="image--center mx-auto" width="2542" height="1401" loading="lazy"></p>
<p>Create an <code>api</code> folder inside the Next.js <code>app</code> directory. This folder will contain the API routes used to generate AI content and create or schedule posts using the Late API.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> app &amp;&amp; mkdir api
</code></pre>
<p>Next, create a <code>generate</code> folder inside the <code>api</code> directory and add a <code>route.ts</code> file. Copy the following code into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// 👇🏻 In api/generate/route.ts file</span>
<span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;
<span class="hljs-keyword">import</span> { GoogleGenAI } <span class="hljs-keyword">from</span> <span class="hljs-string">"@google/genai"</span>;

<span class="hljs-keyword">const</span> ai = <span class="hljs-keyword">new</span> GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
    <span class="hljs-keyword">const</span> { prompt } = <span class="hljs-keyword">await</span> req.json();

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> ai.models.generateContent({
            model: <span class="hljs-string">"gemini-3-flash-preview"</span>,
            contents: <span class="hljs-string">`
    You are a social media post generator, very efficient in generating engaging posts for Twitter (X). Given a topic, generate a creative and engaging post that captures attention and encourages interaction. This posts will always be within the character limit of X (Twitter) which is 280 characters, which includes any hashtags or mentions, spaces, punctuation, and emojis.

    The user will provide a topic or theme, and you will generate a post based on that input.
    Here is the instruction from the user:
    "<span class="hljs-subst">${prompt}</span>"`</span>,
        });
        <span class="hljs-keyword">if</span> (!response.text) {
            <span class="hljs-keyword">return</span> NextResponse.json(
                {
                    message: <span class="hljs-string">"Encountered an error generating the post."</span>,
                    success: <span class="hljs-literal">false</span>,
                },
                { status: <span class="hljs-number">400</span> },
            );
        }

        <span class="hljs-keyword">return</span> NextResponse.json(
            { message: response.text, success: <span class="hljs-literal">true</span> },
            { status: <span class="hljs-number">200</span> },
        );
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">return</span> NextResponse.json(
            { message: <span class="hljs-string">"Error generating post."</span>, success: <span class="hljs-literal">false</span> },
            { status: <span class="hljs-number">500</span> },
        );
    }
}
</code></pre>
<p>The <code>api/generate</code> endpoint accepts the user's prompt and generates post content using the <a target="_blank" href="https://ai.google.dev/gemini-api/docs/quickstart#javascript_1">Gemini API.</a></p>
<p>Now you can send a request to the newly created <code>/api/generate</code> endpoint from the <code>NewPost</code> component. Update the <code>handleGeneratePost</code> function as shown below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleGeneratePost = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    setDisableBtn(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/generate"</span>, {
        method: <span class="hljs-string">"POST"</span>,
        headers: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
        },
        body: <span class="hljs-built_in">JSON</span>.stringify({ prompt }),
    });

    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> result.json();
    <span class="hljs-keyword">if</span> (data.success) {
        setUseAI(<span class="hljs-literal">false</span>);
        setContent(data.message);
        setPrompt(<span class="hljs-string">""</span>);
    }
    setDisableBtn(<span class="hljs-literal">false</span>);
};
</code></pre>
<p>The <code>handleGeneratePost</code> function accepts the user's prompt and returns the AI-generated content.</p>
<h2 id="heading-how-to-use-late-api-in-nextjs">How to Use Late API in Next.js</h2>
<p><a target="_blank" href="https://docs.getlate.dev/core/posts#create-a-draft-scheduled-or-immediate-post">Late</a> provides API endpoints that let you create, schedule, and manage posts programmatically. This allows you to integrate social media posting directly into your applications or automation workflows.</p>
<p>To get started, copy your Late API key and the account ID of your social media platforms into the <code>.env.local</code> file:</p>
<pre><code class="lang-bash">LATE_API_KEY=&lt;Late_API_key&gt;
ACCOUNT_ID=&lt;social_media_acct_id&gt;

<span class="hljs-comment"># Gemini API key</span>
GEMINI_API_KEY=&lt;gemini_API_key&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769615724826/e9500298-caa6-41a6-8d83-74d144d5c535.png" alt="Connect Twitter (X) account and copy account ID" class="image--center mx-auto" width="2526" height="1412" loading="lazy"></p>
<p><strong>Note:</strong> In this tutorial, we will be using Twitter (X) as the social media platform for scheduling posts. You can adapt the same workflow to other platforms supported by Late API by updating the platform and accountId values in your API requests.</p>
<p>Create an <code>api/post</code> endpoint to accept post content and schedule or publish posts using the Late API.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> api
mkdir post &amp;&amp; <span class="hljs-built_in">cd</span> post
touch route.ts
</code></pre>
<p>Then, add the following POST method to <code>post/route.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;
<span class="hljs-keyword">import</span> utc <span class="hljs-keyword">from</span> <span class="hljs-string">"dayjs/plugin/utc"</span>;
<span class="hljs-keyword">import</span> dayjs <span class="hljs-keyword">from</span> <span class="hljs-string">"dayjs"</span>;

dayjs.extend(utc);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
    <span class="hljs-keyword">const</span> { content, publishAt } = <span class="hljs-keyword">await</span> req.json();

    <span class="hljs-comment">// Determine if the post should be scheduled or published immediately</span>
    <span class="hljs-keyword">const</span> nowUTC = publishAt ? dayjs(publishAt).utc() : <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">const</span> publishAtUTC = nowUTC ? nowUTC.format(<span class="hljs-string">"YYYY-MM-DDTHH:mm"</span>) : <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://getlate.dev/api/v1/posts"</span>, {
            method: <span class="hljs-string">"POST"</span>,
            headers: {
                Authorization: <span class="hljs-string">`Bearer <span class="hljs-subst">${process.env.LATE_API_KEY}</span>`</span>,
                <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
            },
            body: <span class="hljs-built_in">JSON</span>.stringify({
                content,
                platforms: [
                    {
                        platform: <span class="hljs-string">"twitter"</span>,
                        accountId: process.env.ACCOUNT_ID!,
                    },
                ],
                publishNow: !publishAt,
                scheduledFor: publishAtUTC,
            }),
        });

        <span class="hljs-keyword">const</span> { post, message } = <span class="hljs-keyword">await</span> response.json();

        <span class="hljs-keyword">if</span> (post?._id) {
            <span class="hljs-keyword">return</span> NextResponse.json({ message, success: <span class="hljs-literal">true</span> }, { status: <span class="hljs-number">201</span> });
        }

        <span class="hljs-keyword">return</span> NextResponse.json({ message: <span class="hljs-string">"Error occurred"</span>, success: <span class="hljs-literal">false</span> }, { status: <span class="hljs-number">500</span> });
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">return</span> NextResponse.json({ message: <span class="hljs-string">"Error scheduling post."</span>, success: <span class="hljs-literal">false</span> }, { status: <span class="hljs-number">500</span> });
    }
}
</code></pre>
<p>From the code snippet above:</p>
<ul>
<li><p>The <code>api/post</code> endpoint accepts the post’s content and an optional <code>publishAt</code> time.</p>
</li>
<li><p>If <code>publishAt</code> is <code>null</code>, the post is published immediately. Otherwise, the time is converted to UTC for scheduling.</p>
</li>
<li><p>It then sends a request to the Late API using your API key and the account ID to create or schedule the post on the selected social media platform.</p>
</li>
</ul>
<p>You can also add a <strong>GET</strong> method to the <code>/api/post</code> endpoint to retrieve posts that have already been created or scheduled:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(
            <span class="hljs-string">"https://getlate.dev/api/v1/posts?platform=twitter"</span>,
            {
                method: <span class="hljs-string">"GET"</span>,
                headers: {
                    Authorization: <span class="hljs-string">`Bearer <span class="hljs-subst">${process.env.LATE_API_KEY}</span>`</span>,
                    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
                },
            },
        );

        <span class="hljs-keyword">const</span> { posts } = <span class="hljs-keyword">await</span> response.json();

        <span class="hljs-keyword">return</span> NextResponse.json({ posts }, { status: <span class="hljs-number">200</span> });
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">return</span> NextResponse.json(
            { message: <span class="hljs-string">"Error fetching posts."</span>, success: <span class="hljs-literal">false</span> },
            { status: <span class="hljs-number">500</span> },
        );
    }
}
</code></pre>
<p>Next, update the <code>handlePostSubmit</code> function in <code>NewPost.tsx</code> to send a POST request to <code>/api/post</code>. This will create or schedule the post and notify the user of the result:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handlePostSubmit = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    setDisableBtn(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> now = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();
    <span class="hljs-keyword">const</span> selected = date ? <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(date) : <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">const</span> publishAt = !selected || selected &lt;= now ? <span class="hljs-literal">null</span> : date;

    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/post"</span>, {
        method: <span class="hljs-string">"POST"</span>,
        headers: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> },
        body: <span class="hljs-built_in">JSON</span>.stringify({ content, publishAt }),
    });

    <span class="hljs-keyword">const</span> { message, success } = <span class="hljs-keyword">await</span> result.json();

    <span class="hljs-keyword">if</span> (success) {
        setContent(<span class="hljs-string">""</span>);
        setDate(<span class="hljs-string">""</span>);
        alert(<span class="hljs-string">"Success: "</span> + message);
    } <span class="hljs-keyword">else</span> {
        alert(<span class="hljs-string">"Error: "</span> + message);
    }

    setDisableBtn(<span class="hljs-literal">false</span>);
};
</code></pre>
<p>Finally, fetch all scheduled or published posts and render them in the <code>PostsQueue</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> fetchScheduledPosts = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/post"</span>, {
            method: <span class="hljs-string">"GET"</span>,
            headers: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> },
        });
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();
        setPosts(data.posts);
        setLoading(<span class="hljs-literal">false</span>);
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error fetching scheduled posts:"</span>, error);
        setLoading(<span class="hljs-literal">false</span>);
    }
}, []);

useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchScheduledPosts();
}, [fetchScheduledPosts]);
</code></pre>
<p>🎉 Congratulations! You’ve successfully built an AI-powered social media post scheduler using Next.js, Gemini API, and Late API.</p>
<p>The source code for this tutorial is available on <a target="_blank" href="https://github.com/dha-stix/ai-post-scheduler">GitHub</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pW2GU3r8bTs" 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> </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you’ve learnt how to create and schedule social media posts across multiple platforms using a single scheduling platform, Late, and how to generate AI content using the Gemini API.</p>
<p>The <a target="_blank" href="https://getlate.dev/">Late API</a> is a powerful tool for automating social media tasks, posting at specific intervals, managing multiple accounts, and tracking analytics – all from one platform. By combining it with generative AI models like Gemini and automation tools like n8n or Zapier, you can build automated workflows that keep your audience engaged with minimal effort.</p>
<p>The <a target="_blank" href="https://ai.google.dev/gemini-api/docs/quickstart">Gemini API</a> also makes it easy to integrate AI-powered text, images, or code generation directly into your applications, opening up a wide range of creative possibilities.</p>
<p>Thank you for reading! 🎉</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Social Media Web Authentication using Firebase ]]>
                </title>
                <description>
                    <![CDATA[ User authentication is extremely important in the context of web development. The way users log in affects their overall experience and engagement with an application. It also affects how they initially perceive it. Authentication techniques are cont... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/social-media-based-web-authentication-with-firebase/</link>
                <guid isPermaLink="false">66bb891dc32849d18c5cdca9</guid>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Firebase ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web App Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Thu, 31 Aug 2023 00:12:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/Article-Cover--3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>User authentication is extremely important in the context of web development. The way users log in affects their overall experience and engagement with an application. It also affects how they initially perceive it.</p>
<p>Authentication techniques are continually evolving as social media sites continue to grow in popularity. The ability to log into web apps using social network accounts is a helpful advance in this area.</p>
<p>This article discusses how you can enhance the user login process for web applications by employing social media authentication through Firebase. It goes into the benefits, setup methods, and integration approaches while offering helpful guidelines.</p>
<h2 id="heading-heres-what-well-cover">Here's what we'll cover:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-why-use-social-media-authentication">Why Use Social Media Authentication?</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-what-is-firebase-and-why-use-it-for-authentication">What is Firebase and Why Use it for Authentication?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-firebase-for-social-media-authentication">How to Set Up Firebase for Social Media Authentication</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-react-app">How to Set Up Your React App</a></li>
<li><a class="post-section-overview" href="#heading-how-to-integrate-social-media-authentication-in-your-app">How to Integrate Social Media Authentication in Your App</a></li>
<li><a class="post-section-overview" href="#heading-striking-the-right-balance-offering-both-social-media-and-emailpassword-authentication">Offering Both Social Media and Email/Password Authentication</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-why-use-social-media-authentication">Why Use Social Media Authentication?</h2>
<p>I'm sure you've grown weary of the usual username-password routine when logging into a new platform. It often entails creating a new password on the spot or resorting to insecure password conventions that could grant unauthorized access to your many accounts.</p>
<p>Fortunately, social media authentication offers some advantages:</p>
<ol>
<li>Effortless User Experience: Social media login choices simplify the registration process, making it convenient for users to initiate their app usage.</li>
<li>Heightened Security: Social media platforms implement strong security measures that can bolster the safety of your app's users.</li>
<li>Elimination of Password Hassles: Through social media authentication, users are relieved from the burden of remembering numerous passwords, reducing the inconvenience of managing credentials.</li>
<li>Reduced Account Abandonment: Social media login prompts users to join and interact with your app, minimizing the chances of them leaving the registration process unfinished.</li>
<li>Access to Trustworthy User Information: Social media platforms provide substantial user information, which can be harnessed to personalize the experience offered by your app.</li>
<li>Streamlined Account Recovery: In instances of forgotten passwords, social media authentication presents a straightforward approach for users to regain entry to their accounts.</li>
</ol>
<p>In summary, social media authentication offers a convenient and secure method for users to join and use your app. It leads to an improved user experience, and decreased account abandonment, and grants you access to valuable user insights.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article is intended for those with a solid grasp of the following concepts:</p>
<ul>
<li>HTML, CSS, and JavaScript</li>
<li>React and React Routing</li>
<li>Fundamental familiarity with using Firebase</li>
</ul>
<h2 id="heading-what-is-firebase-and-why-use-it-for-authentication">What is Firebase and Why Use it for Authentication?</h2>
<p>Firebase serves as a comprehensive platform, providing developers with backend services and tools to create web and mobile applications. </p>
<p>One of its key offerings is an authentication service that streamlines the process of integrating authentication features into apps. </p>
<p>With <a target="_blank" href="https://firebase.google.com/">Firebase</a>, implementing authentication becomes more straightforward, thanks to its provision of pre-built user interface components, developer-friendly APIs, and support for various authentication methods.</p>
<h2 id="heading-how-to-set-up-firebase-for-social-media-authentication">How to Set Up Firebase for Social Media Authentication</h2>
<h3 id="heading-step-1-create-a-firebase-project">Step 1: Create a Firebase Project</h3>
<ol>
<li>Go to <a target="_blank" href="https://console.firebase.google.com/">the Firebase Console</a> and sign in with your Google account.</li>
<li>Click the "Add Project" button.</li>
<li>Enter a name for your project and select a location for your data storage.</li>
<li>Click the "Create" button.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-22-082447.png" alt="Image" width="600" height="400" loading="lazy">
<em>Firebase console homepage</em></p>
<h3 id="heading-step-2-register-a-web-app">Step 2: Register a Web app</h3>
<p>This feature enables you to register your web applications to access the features of Firebase via web apps.</p>
<ol>
<li>In the Firebase Console, click the "Web" (&lt;/&gt;) icon.</li>
<li>Click the "Add App" button.</li>
<li>Enter a name for your app and select the "Web" app type.</li>
<li>Click the "Register" button.</li>
</ol>
<p>After you have created a Firebase project and registered a web app, you can start using Firebase for social media authentication.</p>
<h3 id="heading-step-3-discover-social-media-sign-in-methods">Step 3: Discover Social Media Sign-In Methods</h3>
<p>To do this, once your project is created, you'll need to navigate to the "Authentication" section on the left-hand menu.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/2-Auth-side-bar-shown.png" alt="Image" width="600" height="400" loading="lazy">
<em>Showing the Authentication sidebar</em></p>
<p>Under the "Sign-in method" tab, you'll find a list of authentication providers from which you can choose one:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-22-082758.png" alt="Image" width="600" height="400" loading="lazy">
<em>Displaying various Authentication methods</em></p>
<h3 id="heading-step-4-configure-social-media-providers">Step 4: Configure Social Media Providers</h3>
<h4 id="heading-how-to-configure-google-auth">How to configure Google auth:</h4>
<p>To configure Google auth, simply add a support email, and you’re all set.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/google-enable.png" alt="Image" width="600" height="400" loading="lazy">
<em>Adding a support mail for google auth</em></p>
<h4 id="heading-how-to-configure-github-auth">How to configure GitHub auth:</h4>
<p>To configure GitHub auth, you'll need a Client ID and Client Secret. To get these, sign in to your <a target="_blank" href="https://github.com/">GitHub account</a> and go to Settings &gt; Developer settings.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/github--settings.png" alt="Image" width="600" height="400" loading="lazy">
<em>Github settings panel</em></p>
<p>Then, navigate to OAuth and create a new OAuth application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Setting-up-Github-OAuth.png" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a github OAuth application</em></p>
<p>In order to get the Authorization callback, go back to your Firebase console and copy the URL in the GitHub setup.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/github-callback-url.png" alt="Image" width="600" height="400" loading="lazy">
<em>GitHub callback URL</em></p>
<p>Note: To complete this process, you’d need to have your app already hosted or at least a URL to where your app is going to be hosted.</p>
<p>Next, you’ll be routed to a page where your app has been registered and you have your Client ID and Secret.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/3-Github-Client-ID-and-Secret-generated.png" alt="Image" width="600" height="400" loading="lazy">
<em>GitHub Client ID and secret Generated</em></p>
<p>Copy those details and use them to register GitHub as an auth service on Firebase.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/4-Filling-in-github-details-in-fb.png" alt="Image" width="600" height="400" loading="lazy">
<em>Filling GitHub details on Firebase</em></p>
<h4 id="heading-how-to-configure-twitter-auth">How to configure Twitter auth:</h4>
<p>Similar to Github, start by logging into your Twitter developer account. If you don’t have one, sign up with the <a target="_blank" href="https://developer.twitter.com/en/portal/petition/essential/basic-info">Twitter Developer Portal</a>. It looks something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Twitter-Developers-signup.png" alt="Image" width="600" height="400" loading="lazy">
<em>Twitter Developer Signup</em></p>
<p>After filling in the details, you’ll be routed to the homepage.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/5-twitter-dev-homepage.png" alt="Image" width="600" height="400" loading="lazy">
<em>Twitter Dev homepage</em></p>
<p>Click on your default app, and set up user authentication.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Twitter-user-auth-setup.png" alt="Image" width="600" height="400" loading="lazy">
<em>Twitter OAuth app setup</em></p>
<p>Don’t forget to get the callback URL from Firebase and set the Website URL to the URL where your app is hosted.</p>
<p>After setting it up, navigate to your project’s keys and tokens and generate new ones.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Screenshot-2023-08-25-164558.png" alt="Image" width="600" height="400" loading="lazy">
<em>Generating new App Key and Secret</em></p>
<p>Paste those details back in Firebase to set up Twitter auth. </p>
<p>And with that, your three social media platforms have be set up for authentication.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/All-auths-setup.png" alt="Image" width="600" height="400" loading="lazy">
<em>All Auth's set up</em></p>
<h2 id="heading-ia"> </h2>
<p>How to Set Up Your React App</p>
<p>Now we need to get your React app set up. You'll start by creating a new React app using <a target="_blank" href="https://vitejs.dev/guide/">Vite</a>.</p>
<p>Create a folder on your computer and open that folder with your preferred IDE. Open that IDE’s terminal and run this command:</p>
<pre><code class="lang-bash">npm create vite@latest
</code></pre>
<p>When the details load, select React and wait for the installation to complete.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Vite-React.png" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a React App with Vite</em></p>
<p>You’ll be left with a handful of files and some boilerplate code that you can get rid of.</p>
<p>Next, run <code>npm run dev</code> in the terminal to start a development server on port <code>http://localhost:5173/</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Vit-setup.png" alt="Image" width="600" height="400" loading="lazy">
<em>React app running in browser</em></p>
<p>To use Firebase in your app, you must first define a Firebase config file. This file contains all the necessary data used to identify your Firebase app.</p>
<p>So create a folder in your <code>src</code> directory called <code>firebase</code>. Then nest a <code>config.js</code> file in that folder and paste the config file details from your Firebase console you saved earlier.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/6-firebase-config-in-vscode.png" alt="Image" width="600" height="400" loading="lazy">
<em>Firebase config details</em></p>
<p>Finally, install Firebase via your terminal to use its services in your app.</p>
<pre><code class="lang-bash">npm i firebase
</code></pre>
<h2 id="heading-how-to-integrate-social-media-authentication-in-your-app">How to Integrate Social Media Authentication in Your App</h2>
<p>Considering how large this section would be, it’ll be divided into several sub-sections.</p>
<ol>
<li>Setting up the UI logic for authentication</li>
<li>Setting up the Authentication logic</li>
<li>Implementing Global Authentication State</li>
<li>Creating a custom hook for Social Media Authentication</li>
<li>Creating Routes and Implementing Routing</li>
<li>Social Media Authentication</li>
<li>Route Guarding via the User State</li>
<li>Creating a useLogout hook</li>
<li>Testing the logout functionality</li>
</ol>
<h3 id="heading-how-to-set-up-the-ui-logic-for-authentication">How to set up the UI logic for authentication</h3>
<p>Create a folder (pages) in the <code>src</code> directory that houses the pages you want in your application.</p>
<p>For this implementation, there will be 2 files in the pages folder, <code>Auth.jsx</code> and <code>Home.jsx</code>. These files will act as the pages the user can see either when authenticated or not.</p>
<h3 id="heading-how-to-set-up-the-authentication-logic">How to set up the authentication logic</h3>
<p>Start by importing and initializing Firebase auth, as well as the social media platforms enabled on Firebase in your config.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> {
  getAuth,
  GoogleAuthProvider,
  GithubAuthProvider,
  TwitterAuthProvider,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/auth"</span>;

<span class="hljs-comment">// Initialize Firebase</span>
<span class="hljs-keyword">const</span> app = initializeApp(firebaseConfig);
<span class="hljs-keyword">const</span> auth = getAuth(app);

<span class="hljs-keyword">const</span> googleProvider = <span class="hljs-keyword">new</span> GoogleAuthProvider();
<span class="hljs-keyword">const</span> githubProvider = <span class="hljs-keyword">new</span> GithubAuthProvider();
<span class="hljs-keyword">const</span> twitterProvider = <span class="hljs-keyword">new</span> TwitterAuthProvider();
</code></pre>
<p>Then export these initialized functions to use them in other parts of your application.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> { auth, googleProvider, githubProvider, twitterProvider };
</code></pre>
<h3 id="heading-how-to-implement-global-authentication-state">How to implement Global Authentication State</h3>
<p>To ensure a consistent authentication state throughout your application, consider using the React Context approach.</p>
<h4 id="heading-step-1-create-an-authcontext">Step 1: Create an AuthContext</h4>
<p>Start by generating a context folder within your src directory and then create an <code>AuthContext.jsx</code> file within it. In the <code>AuthContext</code> file, import essential hooks from React and Firebase.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { createContext, useReducer, useEffect, useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { auth } <span class="hljs-keyword">from</span> <span class="hljs-string">"../firebase/config"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AuthContext = createContext();
</code></pre>
<h4 id="heading-step-2-define-a-reducer-function">Step 2: Define a reducer function</h4>
<p>Construct a reducer function to manage state changes for authentication-related actions using the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authReducer = <span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
  <span class="hljs-keyword">switch</span> (action.type) {
    <span class="hljs-comment">// When the action type is "LOGIN", update the state with the new user information</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"LOGIN"</span>:
      <span class="hljs-keyword">return</span> { ...state, <span class="hljs-attr">user</span>: action.payload };

    <span class="hljs-comment">// When the action type is "LOGOUT", update the state to remove the user information</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"LOGOUT"</span>:
      <span class="hljs-keyword">return</span> { ...state, <span class="hljs-attr">user</span>: <span class="hljs-literal">null</span> };

    <span class="hljs-comment">// When the action type is "AUTH_IS_READY", update the state with user information and</span>
    <span class="hljs-comment">// set a state to indicate that the authentication process is complete</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"AUTH_IS_READY"</span>:
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">user</span>: action.payload, <span class="hljs-attr">authIsReady</span>: <span class="hljs-literal">true</span> };

    <span class="hljs-comment">// For any other action type, return the current state without any changes</span>
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> state;
  }
};
</code></pre>
<h4 id="heading-step-3-create-authcontextprovider-component">Step 3: Create AuthContextProvider Component</h4>
<p>Create a provider component that wraps your entire App component, using the reducer for authentication state management.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useEffect, useReducer } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { onAuthStateChanged } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/auth"</span>; 


<span class="hljs-comment">// Authentication context provider component</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AuthContextProvider = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-comment">// Initialize authentication state using a reducer</span>
  <span class="hljs-keyword">const</span> [state, dispatch] = useReducer(authReducer, {
    <span class="hljs-attr">user</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">authIsReady</span>: <span class="hljs-literal">false</span>,
  });

  <span class="hljs-comment">// Effect to determine initial authentication state and update context</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Subscribe to authentication state changes</span>
    <span class="hljs-keyword">const</span> unsub = onAuthStateChanged(auth, <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
      <span class="hljs-comment">// Dispatch an action to update the state with the user information</span>
      dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"AUTH_IS_READY"</span>, <span class="hljs-attr">payload</span>: user });

      <span class="hljs-comment">// Unsubscribe to avoid further unnecessary updates</span>
      unsub(); <span class="hljs-comment">// Unsubscribe once the initial auth state is determined</span>
    });
  }, []);

  <span class="hljs-comment">// Provide authentication state and dispatch function to children components</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AuthContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">...state</span>, <span class="hljs-attr">dispatch</span> }}&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">AuthContext.Provider</span>&gt;</span></span>
  );
};
</code></pre>
<h4 id="heading-step-4-implement-useauthcontext-custom-hook">Step 4: Implement useAuthContext custom hook</h4>
<p>You can simplify access to the authentication context with a custom hook, like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-comment">// Custom hook to access the authentication context</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useAuthContext</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Get the authentication context from the nearest AuthContextProvider</span>
  <span class="hljs-keyword">const</span> context = useContext(AuthContext);

  <span class="hljs-comment">// Check if the context was successfully obtained</span>
  <span class="hljs-keyword">if</span> (!context) {
    <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"useAuthContext must be used inside an AuthContextProvider"</span>);
  }

  <span class="hljs-comment">// Return the authentication context object for use in components</span>
  <span class="hljs-keyword">return</span> context;
}
</code></pre>
<h4 id="heading-how-to-integrating-the-authcontextprovider">How to integrating the AuthContextProvider</h4>
<p>Finally, integrate the <code>AuthContextProvider</code> into your main application setup</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.jsx"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> { AuthContextProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"./context/AuthContext.jsx"</span>;

ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)).render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">AuthContextProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">AuthContextProvider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>With that, all parts of your app can access the context values from the <code>AuthContext</code>.</p>
<h3 id="heading-how-to-create-a-custom-hook-for-social-media-authentication">How to create a custom hook for Social Media Authentication</h3>
<p>Firebase authentication processes are similar in pattern and code structure. So it's a good idea to follow the DRY principle and create a utility hook that performs authentication for all social media platforms. This allows you to reuse the same code for each platform, making your code more efficient and easier to maintain.</p>
<p>Here is the step-by-step process to follow to create a custom hook for social media authentication.</p>
<h4 id="heading-step-1-create-the-custom-hook">Step 1: Create the custom hook</h4>
<p>In your source directory, establish a hooks folder and within it, create a file named <code>useSocialSignup.jsx</code>.</p>
<h4 id="heading-step-2-import-dependencies">Step 2: Import dependencies</h4>
<p>Import the necessary functions from React and Firebase into your <code>useSocialSignup</code> file.</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> { signInWithPopup } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/auth"</span>;
<span class="hljs-keyword">import</span> { auth } <span class="hljs-keyword">from</span> <span class="hljs-string">"../firebase/config"</span>;
<span class="hljs-keyword">import</span> { useAuthContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"../context/AuthContext"</span>;
</code></pre>
<h4 id="heading-step-3-define-the-hook-function">Step 3: Define the hook function</h4>
<p>Develop the <code>useSocialSignup</code> function, which takes a provider as a parameter and returns an object containing an error state, pending state, and the sign-in function for the social provider.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useSocialSignup = <span class="hljs-function">(<span class="hljs-params">provider</span>) =&gt;</span> {
  <span class="hljs-comment">// State variables to manage sign-up process</span>
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [isPending, setIsPending] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [isCancelled, setIsCancelled] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-comment">// Accessing the authentication context's dispatch function</span>
  <span class="hljs-keyword">const</span> { dispatch } = useAuthContext();

  <span class="hljs-comment">// Function to initiate the social sign-up process</span>
  <span class="hljs-keyword">const</span> signInWithSocial = <span class="hljs-keyword">async</span> () =&gt; {
    setError(<span class="hljs-literal">null</span>);
    setIsPending(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> signInWithPopup(auth, provider);

      dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"LOGIN"</span>, <span class="hljs-attr">payload</span>: res.user });

      <span class="hljs-keyword">if</span> (!isCancelled) {
        setIsPending(<span class="hljs-literal">false</span>);
        setError(<span class="hljs-literal">null</span>);
      }
    } <span class="hljs-keyword">catch</span> (err) {
      setError(err.message);
      setIsPending(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-comment">// Effect hook to set isCancelled to true when component unmounts</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> setIsCancelled(<span class="hljs-literal">true</span>);
  }, []);

  <span class="hljs-comment">// Return values and functions for component usage</span>
  <span class="hljs-keyword">return</span> { error, isPending, signInWithSocial };
};
</code></pre>
<p>This hook encapsulates the process of signing in with social providers. It manages error, pending, and cancellation states, interacts with Firebase authentication, and utilizes the authentication context to dispatch actions.</p>
<h3 id="heading-how-to-create-routes-and-implementing-routing">How to create routes and implementing routing</h3>
<p>To ensure smooth navigation and user experience, setting up routes becomes crucial after implementing authentication logic. These well-organized steps guide you through the process.</p>
<h4 id="heading-step-1-install-react-router-dom">Step 1: Install react-router-dom</h4>
<p>Install <a target="_blank" href="https://www.npmjs.com/package/react-router-dom">the react-router-dom package</a>, a popular choice for managing routing in React applications.</p>
<pre><code class="lang-bash">npm i react-router-dom
</code></pre>
<h4 id="heading-step-2-import-dependencies-1">Step 2: Import dependencies</h4>
<p>In your <code>App.jsx</code> file, import necessary components and functions for routing.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { BrowserRouter, Navigate, Route, Routes } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Home"</span>;
<span class="hljs-keyword">import</span> Auth <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Auth"</span>;
</code></pre>
<h4 id="heading-step-3-define-routes">Step 3: Define routes</h4>
<p>Wrap your application content in a BrowserRouter component and use the Routes component to define your routes. Utilize the Route component to map each route path to its corresponding component.</p>
<pre><code class="lang-js"><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">BrowserRouter</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/auth"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Auth</span> /&gt;</span>} /&gt;
      <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}
</code></pre>
<p>At the moment, you can freely navigate between routes, like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Onauth-routing.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Moving between Routes without Auth</em></p>
<h3 id="heading-social-media-authentication">Social Media Authentication</h3>
<p>To ensure your efforts haven't been in vain, head over to the <code>Auth.jsx</code> to implement authentication.</p>
<h4 id="heading-step-1-import-dependencies">Step 1: Import dependencies</h4>
<p>In your <code>Auth.jsx</code> file, start by importing necessary providers, context, and the custom signup hook.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> {
  googleProvider,
  twitterProvider,
  githubProvider,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"../firebase/config"</span>;
<span class="hljs-keyword">import</span> { useSocialSignup } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useSocialSignup"</span>;
<span class="hljs-keyword">import</span> {useEffect} <span class="hljs-keyword">from</span> ‘react’

<span class="hljs-keyword">import</span> {useAuthContext} <span class="hljs-keyword">from</span> “../context/AuthContext”
</code></pre>
<h4 id="heading-step-2-create-instances-of-the-hook">Step 2: Create instances of the hook</h4>
<p>Create instances of the <code>useSocialSignup</code> custom hook for each authentication provider.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> google = useSocialSignup(googleProvider);
<span class="hljs-keyword">const</span> twitter = useSocialSignup(twitterProvider);
<span class="hljs-keyword">const</span> github = useSocialSignup(githubProvider);
</code></pre>
<h4 id="heading-step-3-add-buttons-for-social-sign-up">Step 3: Add buttons for social sign-up</h4>
<p>Create buttons for each social sign-up option (Google, Twitter, GitHub) and attach onClick event handlers to call the <code>signInWithSocial</code> function from the respective hook.</p>
<pre><code class="lang-js"><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">"utility__page"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Welcome to my Auth Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{google.signInWithSocial}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleIcon}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Google<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{twitter.signInWithSocial}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{TwitterIcon}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Twitter<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{github.signInWithSocial}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GithubIcon}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <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>
);
</code></pre>
<h4 id="heading-step-4-apply-styling">Step 4: Apply styling</h4>
<p>You can use the provided CSS below to style your components for a clean and organized appearance.</p>
<pre><code class="lang-css">* {
  <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">html</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">62.5%</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#121212</span>;
}

<span class="hljs-selector-class">.utility__page</span> {
  <span class="hljs-attribute">display</span>: flex;
  <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">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Segoe UI"</span>, Tahoma, Geneva, Verdana, sans-serif;
  <span class="hljs-attribute">row-gap</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#e2dbd9</span>;
}

<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">5rem</span>;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">4rem</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</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-attribute">gap</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-tag">button</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.user</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3rem</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">column-gap</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-class">.logout</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">208</span>, <span class="hljs-number">84</span>, <span class="hljs-number">84</span>);
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
}

<span class="hljs-selector-class">.profile_img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">5rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">5rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
}
</code></pre>
<p>At the moment, your auth page looks something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Auth-page.png" alt="Image" width="600" height="400" loading="lazy">
<em>Auth page after applying styling</em></p>
<h4 id="heading-step-5-test-the-authentication">Step 5: Test the authentication</h4>
<p>To test authentication, import the user from your <code>AuthContext</code> and log it to the console using a <code>useEffect</code>.</p>
<pre><code class="lang-js">  <span class="hljs-keyword">const</span> { user } = useAuthContext();
  useEffect(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(user), [user]);
</code></pre>
<p>Testing the auth now gives the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/first-login-giffy.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Authentication confirmed in the console via the user object</em></p>
<p>As you can see, you’ve successfully logged a user in using Social Media Authentication. Kudos!</p>
<p>To confirm, head over to your Firebase auth page and check for valid users.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Valid-users-check.png" alt="Image" width="600" height="400" loading="lazy">
<em>Confirming signed up user on Firebase</em></p>
<p>Feel free to try other login methods as they all work the same.</p>
<h3 id="heading-route-gaurding-via-the-user-state">Route Gaurding via the User State</h3>
<p>To prevent unauthorized access, set up route guards that check the user's authentication state in your <code>App.jsx</code> file.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { user, authIsReady } = useAuthContext();

<span class="hljs-keyword">if</span> (!authIsReady) {
  <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; <span class="hljs-comment">// Return null while waiting for authIsReady</span>
}

<span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
      {user ? (
        <span class="hljs-tag">&lt;&gt;</span>
          {/* Authenticated routes */}
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
          {/* Route guards */}
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Navigate</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/&gt;</span>
      ) : (
        <span class="hljs-tag">&lt;&gt;</span>
          {/* Authentication routes */}
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/auth"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Auth</span> /&gt;</span>} /&gt;
          {/* Route guards */}
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Navigate</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/auth"</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
    &lt;/Routes&gt;
  &lt;/BrowserRouter&gt;
);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/plain-home-page-after-auth.png" alt="Image" width="600" height="400" loading="lazy">
<em>Routed to home page after adding route guards</em></p>
<p>As you can see, you’ve been routed to the home page, and even if you attempt to go the auth page, you’d be routed back here.</p>
<h4 id="heading-how-to-customize-the-home-page">How to customize the home page</h4>
<p>For the home page, fetch the user's details and display them if a user is authenticated.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useAuthContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"../context/AuthContext"</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">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { user } = useAuthContext();
  <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">"utility__page "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Home Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {user &amp;&amp; (
        <span class="hljs-tag">&lt;&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"user"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> You<span class="hljs-symbol">&amp;apos;</span>re logged in as: <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.displayName} <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"profile_img"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{user.photoURL}</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>&gt;</span>
         <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Auth-showing-details.png" alt="Image" width="600" height="400" loading="lazy">
<em>Home page showing custom user details</em></p>
<p>And voilà! You’ve been able to fetch some details about that user based on the info on the social media they used to log in.</p>
<h3 id="heading-how-to-create-a-uselogout-hook">How to create a useLogout hook</h3>
<p>The final step to complete your authentication process is to provide users with the ability to log out of your application. Here's how to create a useLogout hook:</p>
<h4 id="heading-step-1-create-the-hook">Step 1: Create the hook</h4>
<p>Create a new file called useLogout.jsx in your hooks folder. Import the necessary hooks and functions.</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> { auth } <span class="hljs-keyword">from</span> <span class="hljs-string">"../firebase/config"</span>;
<span class="hljs-keyword">import</span> { signOut } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/auth"</span>;
<span class="hljs-keyword">import</span> { useAuthContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"../context/AuthContext"</span>;
</code></pre>
<h4 id="heading-step-2-create-the-hook-states">Step 2: Create the hook states</h4>
<p>Create states to manage the logout process, including error, pending, and cancellation states.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Error state for potential errors during logout </span>
<span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>); 
<span class="hljs-comment">// State to indicate if logout is in progress </span>
<span class="hljs-keyword">const</span> [isPending, setIsPending] = useState(<span class="hljs-literal">false</span>); 
<span class="hljs-comment">// State to track if the operation is cancelled</span>
<span class="hljs-keyword">const</span> [isCancelled, setIsCancelled] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<h4 id="heading-step-3-extract-the-dispatch-function-from-the-authentication-context">Step 3: Extract the dispatch function from the authentication context</h4>
<p>This function will be used to indicate a logout action has been called:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { dispatch } = useAuthContext();
</code></pre>
<h4 id="heading-step-4-create-the-hook-logic">Step 4: Create the hook logic</h4>
<p>Use a try-catch block create the logic of logging a user out:</p>
<pre><code class="lang-js"><span class="hljs-keyword">try</span> { 
<span class="hljs-comment">// Initiating the logout using Firebase's signOut function </span>
    <span class="hljs-keyword">await</span> signOut(auth); 
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"LOGOUT"</span> }); <span class="hljs-comment">// Dispatching a LOGOUT action </span>
<span class="hljs-comment">// If the operation wasn't cancelled, reset pending state and error </span>
   <span class="hljs-keyword">if</span> (!isCancelled) { 
       setIsPending(<span class="hljs-literal">false</span>); <span class="hljs-comment">// Resetting isPending after the asynchronous call completes </span>
       setError(<span class="hljs-literal">null</span>); <span class="hljs-comment">// Clearing any error that might have occurred </span>
     } 
   } <span class="hljs-keyword">catch</span> (err) { 
      <span class="hljs-comment">// Handling logout error </span>
     <span class="hljs-keyword">if</span> (!isCancelled) { 
        <span class="hljs-built_in">console</span>.log(err.message); <span class="hljs-comment">// Logging the error message</span>
        setError(err.message); <span class="hljs-comment">// Setting the error state in case of an error </span>
        setIsPending(<span class="hljs-literal">false</span>); <span class="hljs-comment">// Resetting pending state if an error occurs </span>
   } 
}
</code></pre>
<h4 id="heading-step-5-how-to-handle-unmounting">Step 5: How to handle unmounting</h4>
<p>In the case where the component is unmounted (the page closes or there’s a route change), you’ll want to handle that occurrence to prevent errors.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Effect hook to set isCancelled to true when component unmounts</span>
   useEffect(<span class="hljs-function">() =&gt;</span> { 
       <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> setIsCancelled(<span class="hljs-literal">true</span>); <span class="hljs-comment">// The cleanup function runs when the component unmounts }, []);</span>
</code></pre>
<h4 id="heading-step-5-exporting-values">Step 5: Exporting values</h4>
<p>Return the relevant values and functions for other components to use.</p>
<pre><code class="lang-js"> <span class="hljs-keyword">return</span> { logout, error, isPending };
</code></pre>
<p>For ease of accesibility, here’s the full useLogout hook.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Importing necessary hooks and functions</span>
<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> { auth } <span class="hljs-keyword">from</span> <span class="hljs-string">"../firebase/config"</span>; <span class="hljs-comment">// Importing Firebase auth instance</span>
<span class="hljs-keyword">import</span> { signOut } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/auth"</span>; <span class="hljs-comment">// Importing signOut function from Firebase</span>
<span class="hljs-keyword">import</span> { useAuthContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"../context/AuthContext"</span>; <span class="hljs-comment">// Importing the custom hook to access the authentication context</span>

<span class="hljs-comment">// Custom hook for handling user logout</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useLogout = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// State variables to manage logout process</span>
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>); <span class="hljs-comment">// Error state for potential errors during logout</span>
  <span class="hljs-keyword">const</span> [isPending, setIsPending] = useState(<span class="hljs-literal">false</span>); <span class="hljs-comment">// State to indicate if logout is in progress</span>
  <span class="hljs-keyword">const</span> [isCancelled, setIsCancelled] = useState(<span class="hljs-literal">false</span>); <span class="hljs-comment">// State to track if the operation is cancelled</span>
  <span class="hljs-keyword">const</span> { dispatch } = useAuthContext(); <span class="hljs-comment">// Accessing the authentication context's dispatch function</span>

  <span class="hljs-comment">// Function to initiate the logout process</span>
  <span class="hljs-keyword">const</span> logout = <span class="hljs-keyword">async</span> () =&gt; {
    setError(<span class="hljs-literal">null</span>); <span class="hljs-comment">// Clearing any previous errors</span>
    setIsPending(<span class="hljs-literal">true</span>); <span class="hljs-comment">// Indicating that the logout process is in progress</span>

    <span class="hljs-keyword">try</span> {
      <span class="hljs-comment">// Initiating the logout using Firebase's signOut function</span>
      <span class="hljs-keyword">await</span> signOut(auth);
      dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"LOGOUT"</span> }); <span class="hljs-comment">// Dispatching a LOGOUT action</span>

      <span class="hljs-comment">// If the operation wasn't cancelled, reset pending state and error</span>
      <span class="hljs-keyword">if</span> (!isCancelled) {
        setIsPending(<span class="hljs-literal">false</span>); <span class="hljs-comment">// Resetting isPending after the asynchronous call completes</span>
        setError(<span class="hljs-literal">null</span>); <span class="hljs-comment">// Clearing any error that might have occurred</span>
      }
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-comment">// Handling logout error</span>
      <span class="hljs-keyword">if</span> (!isCancelled) {
        <span class="hljs-built_in">console</span>.log(err.message); <span class="hljs-comment">// Logging the error message</span>
        setError(err.message); <span class="hljs-comment">// Setting the error state in case of an error</span>
        setIsPending(<span class="hljs-literal">false</span>); <span class="hljs-comment">// Resetting pending state if an error occurs</span>
      }
    }
  };

  <span class="hljs-comment">// Effect hook to set isCancelled to true when component unmounts</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> setIsCancelled(<span class="hljs-literal">true</span>); <span class="hljs-comment">// The cleanup function runs when the component unmounts</span>
  }, []);

  <span class="hljs-comment">// Returning the relevant values and functions for component usage</span>
  <span class="hljs-keyword">return</span> { logout, error, isPending };
};
</code></pre>
<h3 id="heading-how-to-test-the-logout-functionality">How to test the logout functionality</h3>
<p>In your Home.jsx component, import the useLogout hook and extract the logout function. Attach the logout function to a button's <code>onClick</code> event to enable users to log out.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useLogout } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useLogout"</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">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { user } = useAuthContext();
  <span class="hljs-keyword">const</span> { logout } = useLogout(); <span class="hljs-comment">//logout function extracted</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">"utility__page "</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Home Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {user &amp;&amp; (
        <span class="hljs-tag">&lt;&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"user"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> You<span class="hljs-symbol">&amp;apos;</span>re logged in as: <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.displayName} <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"profile_img"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{user.photoURL}</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>&gt;</span>
           //logout function used
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"logout"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{logout}</span>&gt;</span>
             Log out
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<p>At the moment, your home page looks like this;</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/before-logout.png" alt="Image" width="600" height="400" loading="lazy">
<em>Home page before logging the user out</em></p>
<p>Click on the button and log out the user.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/login-out-and-in.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Testing the log in and log out functionality</em></p>
<p>With that, your authentication process is completely set up, congrats!</p>
<h2 id="heading-striking-the-right-balance-offering-both-social-media-and-emailpassword-authentication">Striking the Right Balance: Offering Both Social Media and Email/Password Authentication</h2>
<p>User authentication is a key part of the user experience on any web app. Social media authentication can offer a streamlined experience and enhanced security, but it's important to strike a balance by also offering the option for email/password authentication. This ensures inclusivity, caters to various user preferences, and addresses privacy concerns. </p>
<p>By offering both options, you create a versatile and user-centric authentication process that contributes to a positive user experience.</p>
<p>An example of an ideal authentication page can be seen below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/Final-signup-page.png" alt="Image" width="600" height="400" loading="lazy">
<em>Standard Auth Page</em></p>
<h2 id="heading-guidelines-for-building-auth-pages">Guidelines for Building Auth Pages</h2>
<p>It is important to apply some basics best practices when building authentication pages, such as:</p>
<ol>
<li>Showing all the possible ways a user can get authenticated in a clear and concise manner.</li>
<li>Using authentic company icons to build trust. You can find free company SVGs on sites like <a target="_blank" href="https://fontawesome.com/">Font Awesome</a>, <a target="_blank" href="https://fonts.google.com/icons">Google icons</a>, and so on.</li>
<li>Use intuitive icons to label inputs such as envelope for mail and padlock for password.</li>
<li>Address privacy concerns by clearly communicating how user data will be used and protected during the authentication process.</li>
</ol>
<p>For ease of accessibility, here’s a link to the <a target="_blank" href="https://github.com/Daiveedjay/OAuth-Article">repo</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, using social media login with Firebase is a smart strategy. It brings together user-friendliness, safety, and privacy. </p>
<p>By offering various ways to log in, websites can accommodate different user choices, be more inclusive, and adapt to new trends. </p>
<p>Balancing authentication options like this makes users happy and builds trust. This is important for creating modern websites and ensuring smooth, user-focused logins.</p>
<h3 id="heading-contact-information">Contact Information</h3>
<p>Want to connect or contact me? Feel free to hit me up on the following:</p>
<ul>
<li>Twitter / X : <a target="_blank" href="https://twitter.com/JajaDavid8">@jajadavid8</a></li>
<li>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/david-jaja-8084251b4/">David Jaja</a></li>
<li>Email: Jajadavidjid@gmail.com</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Hackers Attack Social Media Accounts – And How to Defend Against Them ]]>
                </title>
                <description>
                    <![CDATA[ Hey everyone, and welcome to the world of Social Media 📲. In this article, we will explore the famous (or infamous) sphere of social media, why it is critical to both you and hackers, and how you can avoid having your social media accounts attacked.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-protect-social-media-accounts-from-attackers/</link>
                <guid isPermaLink="false">66bb9015a5fd14123a8b4a1e</guid>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ information security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Iwugo ]]>
                </dc:creator>
                <pubDate>Tue, 06 Jun 2023 16:42:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/aman-pal-15r9RAOy38Q-unsplash-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hey everyone, and welcome to the world of Social Media 📲.</p>
<p>In this article, we will explore the famous (or infamous) sphere of social media, why it is critical to both you and hackers, and how you can avoid having your social media accounts attacked.</p>
<p><strong>Disclaimer:</strong> Hacking is a tool with the potential for both good and bad. Under no circumstances should the knowledge in this article be used for any harmful or illegal purposes. Doing so could lead to a long time in a jail cell 💀.</p>
<p>And with that, let’s jump in 🙃.</p>
<h2 id="heading-what-well-cover">What We’ll Cover</h2>
<ol>
<li><p>Overview of Social Media Platforms</p>
</li>
<li><p>Attack Techniques</p>
</li>
<li><p>Defense Tips</p>
</li>
</ol>
<h2 id="heading-overview-of-social-media-platforms">Overview of Social Media Platforms</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/anledry-cobos-D-CYZ9ZaMqs-unsplash.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Media is Everything ¦ Credit:</em> <a target="_blank" href="https://unsplash.com/@anledry"><em>Anledry Cobos</em></a></p>
<p>Meta (formerly Facebook) remains one of the biggest companies on the planet.</p>
<p>Starting off in 2004, it redefined the way we interact with, share, and engage with the world around us. With roughly <a target="_blank" href="https://www.statista.com/statistics/264810/number-of-monthly-active-facebook-users-worldwide/">2.98 billion monthly active users</a>, Facebook has become an integral part of modern society, bridging gaps and fostering virtual communities.</p>
<p>The platform was among the pioneers of the social media craze which introduced the world to more apps such Instagram, Snapchat, Reddit, WhatsApp, YouTube, TikTok, Telegram and most notoriously, Twitter 🐦. Each and every single one of these apps have a different feel and taste to them with one underlying purpose: Connections.</p>
<p>Connections to people, places and products have been the centre of it all. These platforms allow you to interact with friends, as well as strangers. They also help you see the world around you in ways no one thought was possible many years ago. And if you’re a business person or content creator like I am, it allows you to show people what you have to offer.</p>
<p>If an attacker compromises your credentials, they have access to your connections. They could use your data to impersonate you, post illegal and harmful things, damage your reputation, spread malware, and social engineer your friends and followers on the platform in order to steal money and compromise their accounts.</p>
<p><a target="_blank" href="https://blog.gitnux.com/social-media-hacking-statistics/">According to Gitnux</a>, there are about 1.4 billion attacks on social media platforms monthly – quite a lot isn’t it?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-85.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Giga Chad ¦ Credit: The Hacker Community</em></p>
<p>Many companies take the cybersecurity of their infrastructure quite seriously (most times anyway 😶). But as a consumer, you are your own last line of defense or your own greatest vulnerability.</p>
<p>In this article, we will take a look at some ways attackers can convert your ‘connections’ into profit and how you can defend against them. Now let’s find out how hackers can compromise your account.</p>
<h2 id="heading-social-media-account-attack-techniques">Social Media Account Attack Techniques</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/greg-bulla-KItSIXhXFDY-unsplash.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>A ‘Like’ signboard on 1 Hacker Way ¦ Credit: [Greg Bulla](https://unsplash.com/@gregbulla?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer)</em></p>
<h3 id="heading-physical-access">Physical Access</h3>
<p>This may seem obvious, but people still make this mistake a lot. An attacker could install scripts or software that would let them get the passwords of your social media accounts if they have your phone or laptop in their hand.</p>
<p>Software like those from Passrevelator make it easy to get passwords and other credentials from devices on different platforms.</p>
<h3 id="heading-phishing-links-emails-and-sites">Phishing links, emails, and sites</h3>
<p>Phishing is a cyberattack in which the attacker tricks the victim into giving sensitive or critical information through fraudulent websites, forms, links or other means.</p>
<p>It’s pretty easy for anyone to make a Facebook clone with React Native. Tools like <a target="_blank" href="https://github.com/jaykali/maskphish.git">Zphisher</a> and <a target="_blank" href="https://github.com/KasRoudra/PyPhisher">PyPhisher</a> make it even easier for an attacker by setting up a phishing page and creating links to it, too.</p>
<p>As you can see, PyPhisher comes with a wide array of options for some major mayhem.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-86.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The Phyphisher Interface ¦ Credit: Mercury</em></p>
<p>More seasoned criminals can send links in spoofed emails to make them look like they are from official organisations and can register lookalike domains to trick users.</p>
<h3 id="heading-password-spraying-and-bruteforcing">Password Spraying and Bruteforcing</h3>
<p>Passwords are a big security concern, and for good reason. They are often repetitive and easy to guess. Spraying is the process of trying out common passwords while Bruteforcing is the process of trying out all possible combinations to gain access.</p>
<p>Attackers can get the passwords they use in password spraying from common <strong>wordlists</strong>. Wordlists are a list of passwords usually gotten from data breaches. The larger the wordlist, the higher the chances of compromising any account.</p>
<p>Below is a screenshot of the infamous rockyou.txt wordlist from the RockYou hack of 2009.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/rockyou-wordlist.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The rockyou.txt wordlist ¦ Credit Mercury</em></p>
<p>Bruteforcing, on the other hand, involves the attacker generating a custom wordlist alongside usernames or emails on different platforms. This is more effective if the attacker has a specified target.</p>
<p>As you can see, attackers can use a tool known as <strong>crunch</strong> to generate a wordlist, and it has a lot of options.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Crunch-Wordlist.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Crunch in action ¦ Mercury</em></p>
<p>If an attacker uses these techniques on a login page, this has great potential to be an entry point, especially if the site has poor security.</p>
<h3 id="heading-keyloggers">Keyloggers</h3>
<p>A Keylogger is a piece of riskware that keeps track of what a person types on their device. Think of it like your keyboard having a memory card and sending what it stores to an attacker.</p>
<p>Note that keyloggers aren’t inherently bad, as they can also be used for organisational monitoring and parental control. But an attacker does not have authorization to monitor your keystrokes, which makes its use illegitimate.</p>
<p>An attacker could install a keylogger and monitor the victim's keystrokes. All they have to do is wait and read the logs for a peculiar sequence, usually one with an email, followed by a string of characters before the ‘return’ keystroke.</p>
<p>It would usually look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-88.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>A slightly modified Keylogger log ¦ Credit: Mercury</em></p>
<p>Usually, the entire log will be monochrome but for this example I made a few modifications. The red highlight indicates an email account, which is what an attacker would be looking for. Close behind is the password in blue.</p>
<h3 id="heading-network-sniffing">Network Sniffing</h3>
<p>Also known as packet sniffing, this is the practice of intercepting and analysing network packets in order to find out what kind of information is shared within the network.</p>
<p>If connections are not properly encrypted, an attacker could easily obtain sensitive information about the sites visited and the messages and passwords that are sent and inputted in them, respectively. WireShark is one of the most common tools for this kind of attack.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-89.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The Wireshark Interface ¦ Credit: Mercury</em></p>
<h3 id="heading-data-breaches">Data Breaches</h3>
<p>Data breaches are unintentional leaks of sensitive or confidential information. These are usually more devastating to users than organisations and could have far-reaching consequences.</p>
<p>Passwords and login credentials from data leaks can be sold and purchased on the dark web. They are then used to gain unauthorised access to the account and the rest is history.</p>
<h2 id="heading-how-to-defend-against-social-media-attacks">How to Defend Against Social Media Attacks</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/pexels-prateek-katyal-2694434.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>A Neon Instagram Heart ¦ Credit: [Prateek Katyal](https://www.pexels.com/@prateekkatyal/" rel="noopener noreferrer)</em></p>
<p>As you can see, there are many ways to obtain Social Media account credentials. Below are some ways to ensure you are not a victim.</p>
<h3 id="heading-check-the-url">Check the URL</h3>
<p>Always double check any links sent to you via messaging platforms or email. This is a simple but very effective measure against phishing links and sites, as the likelihood of clicking on the wrong link is much lower.</p>
<p>For example, www.facebook.com and www.facebok.com are not the same. As you can observe in the screenshots below, the former is legitimate while an antivirus warns me that the later is a phishing site.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-06-16_21_22-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>facebook.com ¦ Credit: Mercury</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-06-16_16_05-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>facebok.com ¦ Credit: Mercury</em></p>
<h3 id="heading-use-strong-passwordspassphrases">Use strong passwords/passphrases</h3>
<p>Make sure you use strong passwords and don’t use similar passwords for different accounts (not even variants 👀). You can also use passphrases rather than passwords as they are easier to remember but harder to guess or bruteforce.</p>
<p>An example of a password is 'dictionary'. An example of a passphrase is 'mydictionaryisthelargest'. The password is weak and could be guessed or found easily in a wordlist. The passphrase isn't the strongest but it is quite lengthy and would be almost impossible to find in a wordlist or to be guessed.</p>
<h3 id="heading-use-antivirus-software-and-firewalls">Use Antivirus Software and Firewalls</h3>
<p>An Antivirus is a software solution that protects systems against both internal and external threats based on the vendor. A Firewall, on the other hand, protects systems against external threats based on your preferences and settings.</p>
<p>The use of one or both of these products can go a long way in protecting both individuals and organisations from information stealing malware.</p>
<h3 id="heading-vpns">VPNs</h3>
<p>A Virtual Private Network is a secure network connection that connects you to the internet privately and anonymously. This is done by encrypting the connection and routing it through remote servers.</p>
<p>VPNs are a great option to avoid packet sniffers because packets analysed are encrypted. This means it’s going to be quite difficult for an attacker to get passwords from technical gibberish.</p>
<h3 id="heading-tracking-breaches">Tracking Breaches</h3>
<p>Tracking breaches can be done at an individual or enterprise level. It’s effectiveness, however, usually depends on how much you are willing to pay for it.</p>
<p>Individuals can use sites like <a target="_blank" href="http://haveibeenpwned.com/">haveibeenpwned.com</a> to check if their data has been compromised in any breaches and Enterprises can setup security units with the role of constantly monitoring the Internet for breaches related to them.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/pexels-visual-tag-mx-5361087.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Social Media in Scrabble ¦ Credit: [Visual Tag Mx](https://www.pexels.com/@visual-tag-mx-1321732/" rel="noopener noreferrer)</em></p>
<p>Getting credentials is pretty easy with some determination and a touch of mischievousness. But companies have gotten better at defense in recent years and attackers have had to get more creative.</p>
<p>As an individual, you are your last and dare I say best line of defense. Ensure your shields are always up in the online jungle. Stay safe and Happy Hacking 🙃.</p>
<h3 id="heading-acknowledgements">Acknowledgements</h3>
<p>Thanks to <a target="_blank" href="https://twitter.com/Anuoluwap__o">Anuoluwapo Victor</a>, <a target="_blank" href="https://www.linkedin.com/in/chinaza-nwukwa-22a256230/">Chinaza Nwukwa</a>, <a target="_blank" href="https://www.linkedin.com/in/mercy-holumidey-88a542232/">Holumidey Mercy</a>, <a target="_blank" href="https://www.linkedin.com/in/favour-ojo-906883199/">Favour Ojo</a>, <a target="_blank" href="https://www.linkedin.com/in/georgina-awani-254974233/">Georgina Awani</a>, and my family for the inspiration, support and knowledge used to put this together. You’re the best.</p>
<h3 id="heading-resources">Resources</h3>
<ol>
<li><p><a target="_blank" href="https://blog.gitnux.com/social-media-hacking-statistics/">Social Media Attack Statistics</a></p>
</li>
<li><p><a target="_blank" href="https://www.passwordrevelator.net">GUI tools for physical access hacking</a></p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Your Own Mastodon Instance ]]>
                </title>
                <description>
                    <![CDATA[ Mastodon is a decentralized, federated social media platform based on the ActivityPub protocol. It allows you to follow and interact with friends across multiple instances.  In this article, you will learn how freeCodeCamp set up our own Mastodon ins... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-your-own-mastodon-instance/</link>
                <guid isPermaLink="false">66ac7f467d22919a412de7fd</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mastadon ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Fri, 11 Nov 2022 16:29:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/Screen-Shot-2022-11-14-at-4.49.55-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Mastodon is a decentralized, federated social media platform based on the <a target="_blank" href="https://www.w3.org/TR/activitypub/">ActivityPub</a> protocol. It allows you to follow and interact with friends across multiple instances. </p>
<p>In this article, you will learn how freeCodeCamp set up our own Mastodon instance - and how you can too.</p>
<h2 id="heading-what-is-mastodon">What is Mastodon?</h2>
<p>Imagine if there were multiple different websites for Twitter. On each of those websites, you could create an account (create one on all of them, if you were feeling ambitious). </p>
<p>You could then use your account to follow any of your friends on any of the other websites. You could repost their content to your account, and see activity from all of your followed accounts on a single timeline.</p>
<p>While Mastodon is by far the most popular platform to use, there are also other options such as <a target="_blank" href="https://github.com/misskey-dev/misskey">Misskey</a>, <a target="_blank" href="https://git.pleroma.social/pleroma/pleroma">Pleroma</a>, and their various forks. </p>
<p>Some of these platforms will be able to federate with each other, with cross-platform capabilities, while others will not.</p>
<h2 id="heading-getting-started-with-mastadon">Getting Started with Mastadon</h2>
<p>There are a couple of things you will need to get started with the process we followed for freeCodeCamp:</p>
<ul>
<li>A <a target="_blank" href="https://digitalocean.com">DigitalOcean</a> account</li>
<li>A DNS provider (we use <a target="_blank" href="https://cloudflare.com">Cloudflare</a>).</li>
</ul>
<p>Begin by logging in to DigitalOcean and creating a new droplet. In the image options, select the marketplace tab. Then, search for the Mastodon image - this will handle a good portion of the setup for you.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-48.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Configure the rest of the droplet settings to your own needs - for the size, we began with the $12 option with the plan to scale up as necessary.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-49.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once the droplet has finished provisioning, copy the IP address. Set up an A record for your domain or subdomain to point to your droplet - <strong>do this BEFORE you SSH into the droplet</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-50.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-52.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once your DNS is ready, SSH into the droplet as the <code>root</code> user.</p>
<h2 id="heading-how-to-set-up-your-mastadon-instance">How to Set Up Your Mastadon Instance</h2>
<p>When you first SSH into the server, the automatic setup tool will run.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-54.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Follow the prompts:</p>
<ul>
<li>For <code>Domain name</code>, enter the new domain/subdomain record you just set up.</li>
<li>The tool will ask if you want to store user-uploaded files on the cloud. If you do, you'll need to provide credentials for a storage provider such as Amazon S3.</li>
<li>Mastodon requires an SMTP server for email notifications and the registration flow. You can either spin up your own, or provide credentials for a service like <a target="_blank" href="https://sendgrid.com">SendGrid</a>.</li>
<li>The SMTP flow will prompt you to send a test email. This is <em>highly recommended</em>, as this will confirm your SMTP settings are correct. If they are not, the setup wizard will prompt you to re-enter them.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-55.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will next create a Mastodon account to serve as the administrator. This can be your personal account, or it can be a shared account among your team.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-68.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Finally, you'll be prompted to enter your email for LetsEncrypt certificate renewal notifications.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After a few minutes, your instance should be up and running.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-configure-your-mastadon-instance">How to Configure Your Mastadon Instance</h2>
<p>Visiting your new domain/subdomain should display the Mastodon landing page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Sign in using the admin credentials you generated earlier. Then, select <code>Preferences</code> on the right sidebar, followed by <code>Administration</code> -&gt; <code>Site Settings</code> on the left.</p>
<p>Here you can configure the basic information related to your instance, and upload your branding assets.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>These will be displayed on your instance's <code>/about</code> page, shown to users when they register/login (and available in the footer).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-66.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you are ready to start accepting user sign-ups, change the <code>Registration mode</code> setting to either <code>Anyone can sign up</code> or <code>Approval required for sign up</code>.</p>
<h2 id="heading-how-to-manage-users">How to Manage Users</h2>
<p>Under the <code>Moderation</code> -&gt; <code>Accounts</code> tab in the settings, you can see registered and pending users.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-63.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you click on a username, you will be taken to the user management view.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From this screen, you can manage their permission level (that is, grant moderation or admin status), check IP information, and block email domains. </p>
<p>Your moderation team can also leave private notes (only visible to the team) on a user account, to help keep a history of any moderation concerns.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now that your instance is up and running, feel free to poke around the settings and interaction options. For more information on the various options, check out the <a target="_blank" href="https://docs.joinmastodon.org/">official documentation</a>.</p>
<p>You can find the freeCodeCamp core team and volunteer moderators on <a target="_blank" href="https://social.freecodecamp.org">our private instance</a>, where you can follow us from the instance you just created.</p>
<p>Enjoy your new platform, and happy coding!</p>
<p><em>Cover image from Mastodon's <a target="_blank" href="https://blog.joinmastodon.org/2022/06/mastodon-branding-updates/">branding update page</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Unblock Someone on Instagram ]]>
                </title>
                <description>
                    <![CDATA[ If you use Instagram, you may have needed to block certain accounts from time to time. But what if things change and you want to unblock someone?  In this article, I will walk you through the three ways to unblock someone on Instagram using the mobil... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-unblock-someone-on-instagram/</link>
                <guid isPermaLink="false">66b8d9aeeb5fc71c855edb3e</guid>
                
                    <category>
                        <![CDATA[ communication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ instagram ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jessica Wilkins ]]>
                </dc:creator>
                <pubDate>Tue, 31 Aug 2021 21:30:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/dole777-EQSPI11rf68-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you use Instagram, you may have needed to block certain accounts from time to time. But what if things change and you want to unblock someone? </p>
<p>In this article, I will walk you through the three ways to unblock someone on Instagram using the mobile app for iPhone and Android.</p>
<h2 id="heading-how-to-unblock-someone-on-instagram-through-settings">How to unblock someone on Instagram through settings</h2>
<p>The first way you can unblock something is through the settings tab. Here are the steps you'll need to follow:</p>
<ol>
<li>Login to the Instagram app.</li>
<li>Click on the profile icon located at the bottom right hand side.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/profile-on-right.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-profile.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Click on the hamburger menu at the top right hand corner.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-hamburger-menu.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Click on Settings from the dropdown list of options.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-settings.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="5">
<li>Click on Privacy.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-privacy.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="6">
<li>Scroll down and click on Blocked Accounts.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/blocked-accounts.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="7">
<li>Click on Unblock next to the profile.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/unblock-next-to-profile.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="8">
<li>Click the red Unblock text to confirm.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-unblock.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you are successful, then you should see this message:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/unblock-success.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-unblock-someone-on-instagram-through-direct-message">How to unblock someone on Instagram through Direct Message</h2>
<p>If you have corresponded with someone over Direct Message, you can unblock them there. </p>
<ol>
<li>Click on the message icon located at the top right hand corner of your screen. </li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-31-at-12.13.47-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="2">
<li>Click on the profile you want to unblock.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/account-to-block.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Click on Unblock located at the bottom of the message thread.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-unblock-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Click on the red Unblock text to confirm.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/unblock-message.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-unblock-someone-on-instagram-through-their-profile">How to unblock someone on Instagram through their profile</h2>
<ol>
<li>Go the profile of the person you blocked. </li>
<li>Click on the Unblock button in their profile.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/unblock-option-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Click on the red Unblock text to confirm.</li>
</ol>
<h3 id="heading-alternative-option">Alternative option</h3>
<ol>
<li>On their profile, you can click on the three dots located at the top right hand corner. On Android, the three dots might be vertical. </li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/click-three-dots.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/three-dots.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="2">
<li>Click on Unblock.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/unblock-option-2.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Click on the red Unblock text to confirm.</li>
</ol>
<p>Those are three ways you can unblock someone on Instagram. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Webmentions with Gatsby.js – A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ Webmention is a simple protocol developed by the IndieWeb Community that you can use to request notifications when your URLs are mentioned on the web. When you post on your own site and syndicate elsewhere (POSSE), Webmention lets you enable rich int... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-webmentions-with-gatsby-beginners-guide/</link>
                <guid isPermaLink="false">66d4615c4a0edd9b48e83599</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Spruce Emmanuel ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jul 2021 16:33:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_19-7-2021_222358_iamspruce.dev-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://indieweb.org/webmention">Webmention</a> is a simple protocol developed by the IndieWeb Community that you can use to request notifications when your URLs are mentioned on the web.</p>
<p>When you post on your own site and syndicate elsewhere (<a target="_blank" href="https://indieweb.org/POSSE">POSSE</a>), Webmention lets you enable rich interactions on your syndicated posts. This article will walk you through the simplest way to turn your social media interactions into Webmentions and display them on your Gatsby.js site.</p>
<p>This tutorial will implement Webmentions on an already existing site, so you should have at least some basic knowledge of React and Gatsby.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>To help you better understand how Webmentions work, check out the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/new-webmentions-cycle-1.jpg" alt="Stages of sending and receiving webmentions" width="600" height="400" loading="lazy"></p>
<p><em>Different stages of Webmention process</em></p>
<p>Let's break down this these three stages and learn how to implement them.</p>
<h2 id="heading-how-to-send-webmentions">How to Send Webmentions</h2>
<p>A Webmention Sender is an implementation that send's Webmentions. <a target="_blank" href="https://brid.gy/">Bridgy</a> is a good example of a sender.</p>
<p>Bridgy is an open source tool that pulls your social media interactions from popular social sites and blog engines like Twitter, Facebook, Instagram, and Medium and magically turns them into Webmentions.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/bridy-line-1.jpg" alt="Bridgy turning Social media Interactions to Webmentions" width="600" height="400" loading="lazy"></p>
<p><em>Bridgy turning social interactions to Webmentions</em></p>
<p>To use Bridgy, your domain needs to support the <a target="_blank" href="https://indieauth.com/">indieAuth Login process</a>.</p>
<p>In your <code>layout.js</code> component (or from whatever component you are managing your <code>&lt;head&gt;</code> with React Helmet), add the following code to link to your social profiles:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Helmet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-helmet"</span>
<span class="hljs-comment">// other imports</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">Layout</span>(<span class="hljs-params">{ children}</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">"wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"me"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://twitter.com/sprucekhalifa"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"me"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://github.com/iamspruce"</span> /&gt;</span>

      <span class="hljs-tag">&lt;/<span class="hljs-name">Helmet</span>&gt;</span>
...
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>On each of the social services you just linked to, make sure your profile has a link leading back to your homepage like you can see below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/twitter-link-back.jpg" alt="Back links to your website" width="600" height="400" loading="lazy"></p>
<p><em>Adding a link back to you homepage on Twitter</em></p>
<p>That's it! You are done with the IndieAuth login Process. Now head over to <a target="_blank" href="https://brid.gy/">Bridgy</a> and sign in with Twitter (if you used Twitter for your IndieAuth process).</p>
<p>From now on Bridgy will periodically analyze your tweets (<a target="_blank" href="https://brid.gy/about#privacy">I promise it doesn't do anything with your data</a>). For each tweet that has a link to your site, it will get all the replies, likes, retweets, and so on and send them as Webmentions.</p>
<h2 id="heading-how-to-receive-webmentions">How to Receive Webmentions</h2>
<p>A Webmention receiver is an implementation that receives Webmentions to one or more target URLs.</p>
<p>This implementation can be a script executed on your server, but in the case of GatsbyJs which has no server we'll rely on a third party tool called <a target="_blank" href="https://webmention.io/">webmention.io</a> created by <a target="_blank" href="https://aaronparecki.com/">Aaron Parecki</a>.</p>
<p>This Tool receives your Webmentions and stores and organizes them. It also provides an API that you can use to easily grab your Webmentions and display them on your site.</p>
<p>To use webmention.io make sure you followed the IndieAuth Login process in the "How to Send Webmentions" section. Then go to <a target="_blank" href="https://webmention.io/">webmention.io</a> and sign in with your domain name.</p>
<p>Once you have signed up, add the following to the <code>&lt;head&gt;</code> of your site (and just replace <code>username</code> with the username you got when you logged in):</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Helmet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-helmet"</span>
<span class="hljs-comment">// other imports</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">Layout</span>(<span class="hljs-params">{ children}</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">"wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span>&gt;</span>
        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"webmention"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://webmention.io/username/webmention"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"pingback"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://webmention.io/username/xmlrpc"</span> /&gt;</span>

      <span class="hljs-tag">&lt;/<span class="hljs-name">Helmet</span>&gt;</span>
...
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>When you start getting Webmentions from your target URLs, you should see them in your dashboard like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_21-7-2021_51043_webmention.io.jpeg" alt="webmention.io dashboard" width="600" height="400" loading="lazy"></p>
<p><em>webmention.io dashboard</em></p>
<p>You can easily monitor your Webmentions in your dashboard.</p>
<h2 id="heading-how-to-pull-webmentions-data-to-your-website">How to Pull Webmentions Data to Your Website</h2>
<p>This is the fun part where you'll pull your Webmentions data from the Webmention.io API. To do this, I created a Gatsby plugin called <code>[gatsby-source-webmentions](https://www.npmjs.com/package/gatsby-source-webmentions)</code></p>
<p>NOTE: there's another plugin called <code>[gatsby-plugin-webmentions](https://github.com/ChristopherBiscardi/gatsby-plugin-webmention)</code> which pulls in Webmentions data from webmention.io API and makes them available in Graphql.</p>
<h3 id="heading-spruce-if-theres-already-a-gatsby-plugin-why-create-another-one">Spruce, if there's already a Gatsby plugin why create another one?</h3>
<p>I created this plugin for two reasons:</p>
<ol>
<li><p>First because I can, and why not.</p>
</li>
<li><p>Second, for image optimization – this plugin lets you use the gatsby-image plugin to optimize the images returned by the API.</p>
</li>
</ol>
<h3 id="heading-how-to-install-the-plugin">How to Install the Plugin</h3>
<p>To install the plugin, open your system terminal or the VS Code integrated terminal and run the following:</p>
<pre><code class="lang-js">npm install gatsby-source-webmentions
</code></pre>
<p>Next you'll need to add the plugin to your <code>gatsby-config.js</code> plugins array:</p>
<pre><code class="lang-js">   { 
      <span class="hljs-attr">resolve</span>: <span class="hljs-string">"gatsby-source-webmentions"</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">DOMAIN</span>: <span class="hljs-string">"example.com"</span>, <span class="hljs-comment">// without https and any slashes</span>
        <span class="hljs-attr">TOKEN</span>: process.env.WEBMENTIONS_TOKEN, <span class="hljs-comment">// token from webmention.io</span>
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">100</span>, <span class="hljs-comment">// optional</span>
      },
</code></pre>
<p>The plugin takes a few options:</p>
<ol>
<li><p>DOMAIN: the domain name you used to sign in to <a target="_blank" href="http://webmention.io">webmention.io</a></p>
</li>
<li><p>TOKEN: the token you got from your <a target="_blank" href="http://webmention.io">webmention.io</a> dashboard</p>
</li>
<li><p>perPage: the number of Webmentions you want to fetch per page (this is completely optional)</p>
</li>
</ol>
<p>To avoid pushing your secret token to GitHub add it as an <a target="_blank" href="https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/">environmental variable</a>.</p>
<h3 id="heading-how-to-display-webmentions-on-the-client-side">How to Display Webmentions on the Client Side</h3>
<p>If you're dynamically creating your pages with the <a target="_blank" href="https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/#createPages">createPage Node API</a> there's a high chance that you passed the <code>slug</code> variable to all your site pages. If you are unsure or you named it something else just check your site <code>gatsby-node.js</code> file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/graphql-variable.jpg" alt="GatsbyJs generating slug" width="600" height="400" loading="lazy"></p>
<p><em>Using Create pages to generate a slug for site pages</em></p>
<p>In your <code>src/templates/blog.js</code> or wherever your page templates are, you'll query Webmentions only if the <code>wm_slug</code> matches the page <code>slug</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { graphql } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Layout"</span>
<span class="hljs-keyword">import</span> Comment <span class="hljs-keyword">from</span> <span class="hljs-string">"./Comment"</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
        totalCount
        edges {
          node {
            id
            published
            publishedFormated: published(formatString: "MMM Do, YYYY")
            author {
                name
                photo
                url
            }
            url
            wm_id
            content {
              html
            }
          }
        }
      }
    }
`</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">BlogPost</span>(<span class="hljs-params">{ data, location }</span>) </span>{
...
  const mentions = data.allWebmention

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      ...
      // display mentions in a react component
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p><code>wm_slug</code> is a node I created to grab the slug from the <code>wm_target</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/wm_slug.jpg" alt="Image showing wm_target and wm_slug" width="600" height="400" loading="lazy"></p>
<p>Now you can map over all the Webmentions for that target <code>slug</code> and display them in a React Component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { graphql } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Layout"</span>
<span class="hljs-keyword">import</span> Comment <span class="hljs-keyword">from</span> <span class="hljs-string">"./Comment"</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
    // graphql queries
      }
    }
`</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">BlogPost</span>(<span class="hljs-params">{ data }</span>) </span>{
...
  const mentions = data.allWebmention

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      ...
          <span class="hljs-tag">&lt;<span class="hljs-name">ol</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"webmentions__list"</span>&gt;</span>
      {mentions.edges.map(edge =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Comment</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{edge.node.wm_id}</span>
          <span class="hljs-attr">imageUrl</span>=<span class="hljs-string">{edge.node.author.photo}</span>
          <span class="hljs-attr">authorUrl</span>=<span class="hljs-string">{edge.node.author.url}</span>
          <span class="hljs-attr">authorName</span>=<span class="hljs-string">{edge.node.author.name}</span>
          <span class="hljs-attr">dtPublished</span>=<span class="hljs-string">{edge.node.published}</span>
          <span class="hljs-attr">dtPublishedFormated</span>=<span class="hljs-string">{edge.node.publishedFormated}</span>
          <span class="hljs-attr">content</span>=<span class="hljs-string">{edge.node.content</span> &amp;&amp; <span class="hljs-attr">edge.node.content.html</span>}
          <span class="hljs-attr">url</span>=<span class="hljs-string">{edge.node.url}</span>
        /&gt;</span>
      ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ol</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<h3 id="heading-how-to-group-the-webmentions-by-type">How to Group the Webmentions by Type</h3>
<p>Although this is completely optional, it is a good idea to group your Webmentions by type:</p>
<ol>
<li><p><code>"in_reply_to"</code> – for replies</p>
</li>
<li><p><code>"like_of"</code> – for likes</p>
</li>
<li><p><code>"retweet_of"</code> – for retweets, etc.</p>
</li>
</ol>
<pre><code class="lang-js">...

export <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
      likes: group(field: like_of) {
        totalCount
        edges {
          node {
             // node queries
          }
        }
      }

      replies: group(field: in_reply_to) {
        totalCount
        edges {
          node {
            // node queries
        }
      }
    }
}
...</span>
</code></pre>
<h3 id="heading-how-to-optimize-the-webmentions-author-images">How to Optimize the Webmentions Author Images</h3>
<p>I mentioned earlier that the <code>gatsby-source-plugin</code> allows us to optimize the images returned from the Webmentions query.</p>
<p>To be able to optimize the images you must have <code>[gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/)</code>, <code>gatsby-plugin-sharp</code>, <code>gatsby-transformer-sharp</code> and <code>gatsby-source-filesystem</code> installed:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
      likes: group(field: like_of) {
        totalCount
        edges {
          node {
            // other node queries
            author {
                photoSharp {
                  childImageSharp {
                    gatsbyImageData(
                      width: 38
                      placeholder: BLURRED
                      formats: [AUTO, WEBP, AVIF]
                )
              }
             }
            }
          }
        }
      }
    }
`</span>
</code></pre>
<p>For other optimizations and image processing, refer to the <code>gatsby-plugin-image</code> <a target="_blank" href="https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/">reference guide</a>.</p>
<h3 id="heading-how-to-add-custom-pagination">How to Add Custom Pagination</h3>
<p>You can paginate your Webmentions with one of the many Gatsby pagination plugins. But all I wanted was a simple "Load More" button, and thankfully smarter people like <a target="_blank" href="https://www.erichowey.dev/">Eric Howey</a> have already thought about this.</p>
<p>The code below is from the article <a target="_blank" href="https://www.erichowey.dev/writing/load-more-button-and-infinite-scroll-in-gatsby/"><strong>Load more button and infinite scroll in Gatsby</strong></a> (although I made a few adjustments to it):</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { graphql } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>
<span class="hljs-keyword">import</span> Comment <span class="hljs-keyword">from</span> <span class="hljs-string">"./Comment"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
        totalCount
        edges {
          node {
          // node queries
         }
        }
       }
      }

export default function BlogPost({ data }) {
...
  const replies = data.allWebmention

  const [state, setState] = useState({
    list: [...replies.slice(0, 5)],
    Load_more: false,
    has_more: replies.length &gt; 5,
  })
  const handleState = () =&gt; {
    state.Load_more = true
  }
  //handle loading more mentions
  useEffect(() =&gt; {
    if (state.Load_more &amp;&amp; state.has_more) {
      const currentLength = state.list.length
      const is_more = currentLength &lt; replies.length
      const new_list = is_more
        ? replies.slice(currentLength, currentLength + 5)
        : []
      setState.list = [...state.list, ...new_list]
      setState.Load_more = false
    }
  }, [state.Load_more, state.has_more, replies, state.list])

  useEffect(() =&gt; {
    const is_more = state.list.length &lt; replies.length
    setState.has_more = is_more

  }, [state.list,replies.length])
  return (
    &lt;div className="webmentions-wrapper"&gt;
      {replies.length &gt; 0 ? (
        &lt;&gt;
    &lt;h4&gt;Comments &lt;span className="webmentions-counter"&gt;{replies[0].totalCount}&lt;/span&gt; &lt;/h4&gt;
    &lt;ol className="webmentions__list"&gt;
      {state.list.edges.map(edge =&gt; (
        &lt;Comment
          key={edge.node.wm_id}
          imageUrl={edge.node.authorImg}
          authorUrl={edge.node.authorUrl}
          authorName={edge.node.authorName}
          dtPublished={edge.node.published}
          dtPublishedFormated={edge.node.publishedFormated}
          content={edge.node.content &amp;&amp; edge.node.content.html}
          url={edge.node.url}
        /&gt;
      ))}
      &lt;/ol&gt;
      &lt;div className="webmentions-load text-center"&gt;
      {state.has_more ? (
          &lt;Button
            event={handleState}
            name="Load More"
            label="Load More Webmentions"
            btnSize="small"
            btnType="primary"
          /&gt;
      ) : (
        &lt;p&gt;No More Mentions...&lt;/p&gt;
      )}
    &lt;/div&gt;
        &lt;/&gt;
      ) : (
        &lt;p&gt;No Webmentions found&lt;/p&gt;
      )}
    &lt;/div&gt;
  )
}</span>
</code></pre>
<h2 id="heading-continuous-deployment-with-webmentions">Continuous Deployment with Webmentions</h2>
<p>As you might have noticed, your Webmentions data are pulled at build time. This means that users won't see new Webmentions unless your site has been built.</p>
<p>I host my site in Gatsby Cloud and to avoid crawling out of bed at midnight to build my site. It provides us with a WEBHOOK that can trigger a build for your site even while you are sleeping.</p>
<p>If you are using Gatsby cloud, go to your <a target="_blank" href="https://www.gatsbyjs.com/dashboard/">dashboard</a> and copy the Webhook:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_21-7-2021_7512_www.gatsbyjs.com.jpeg" alt="Gatsby Cloud dashboard" width="600" height="400" loading="lazy"></p>
<p><em>Webhooks from Gatsby Cloud dashboard</em></p>
<p>Once you have copied the Webhook, head over to your webmention.io dashboard, click on <a target="_blank" href="https://webmention.io/settings/webhooks">Web hooks</a>, and paste the copied Webhook into the form:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_21-7-2021_72655_webmention.io.jpeg" alt="webmention.io web hook" width="600" height="400" loading="lazy"></p>
<p><em>webmention.io web hooks</em></p>
<p>That's it – you are done. Now any time you get a new Webmention the Web hook will build your site automatically.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial we learned how to implement Webmentions on your Gatsby site. If you have any questions or you found this tutorial useful, please feel free to connect with me on Twitter <a target="_blank" href="https://twitter.com/sprucekhalifa">@sprucekhalifa</a>. Thank you.</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to Code ]]>
                </title>
                <description>
                    <![CDATA[ This morning I woke up to dozens of messages from students who had read an article titled “Please Don’t Learn to Code.” At first, I assumed Jeff Atwood’s 2012 article had spontaneously reappeared on Reddit. But no — this was a brand new Tech Crunch a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/please-do-learn-to-code-233597dd141c/</link>
                <guid isPermaLink="false">66b8d4ede9a1e34f08e4924c</guid>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ education ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Mon, 01 Mar 2021 07:43:00 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*euZOtdFE4rCo0GNq0yOofQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This morning I woke up to dozens of messages from students who had read an article titled “Please Don’t Learn to Code.”</p>
<p>At first, I assumed Jeff Atwood’s 2012 article had spontaneously reappeared on Reddit. But no — this was a brand new Tech Crunch article of the same name, which echoed Atwood’s assertion that encouraging everyone to learn programming is like encouraging everyone to learn plumbing.</p>
<p>Here’s why programming — unlike plumbing — is an important skill that everyone should learn: <strong>programming is how humans talk to machines</strong>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*iET2rwyHLe8ZfXBO9NYHqA.png" alt="Image" width="542" height="506" loading="lazy">
<em>John McCarthy, the computer scientist who invented the Lisp language and coined the term “Artificial Intelligence”</em></p>
<blockquote>
<p>“Everyone needs computer programming. It will be the way we speak to the servants.” — John McCarthy</p>
</blockquote>
<h4 id="heading-people-have-been-managing-other-people-for-thousands-of-years">People have been managing other people for thousands of years.</h4>
<p>The ancient Romans built their empire on the backs of defeated soldiers. The British built their empire by imposing their will on the residents of dozens of colonies. And the US became the economic force it is today thanks in part to cheap immigrant labor during the industrial revolution.</p>
<p>But here in the 21st century, we no longer get work done by managing people who tend grain fields, import spices from Asian colonies, or install railroads across the Rocky Mountains.</p>
<p>Now we get work done by managing machines.</p>
<h2 id="heading-learn-to-code-because-the-nature-of-work-has-fundamentally-changed">Learn to Code – Because the Nature of Work has Fundamentally Changed</h2>
<p>Today, it is no longer humans who do most of the work — it’s machines.</p>
<p>Think about it — every day, humans make 3.5 billion Google searches. It’s machines that carry out that work — not humans.</p>
<p>Think about how many man-hours it would take for humans to conduct even a single Google search manually. Can you imagine a bunch of PhD’s phoning each other around the clock deliberating about which documents they should recommend to whom? This work is only even remotely practical if it’s done by machines.</p>
<p>Trip Advisor helps you decide where to go for vacation. Expedia helps you book the right flight to get there. Google Maps directs you to the airport. All of these services are within the reach of average consumers thanks to the hard work of machines.</p>
<p>But machines are only able to do all this work because humans tell them exactly what to do. And the only way for humans to do this is by writing software.</p>
<p>That’s right — computers are not nearly as smart as humans. For computers to succeed at the jobs we’ve assigned them, they need us humans to give them extremely clear instructions.</p>
<p>That means coding.</p>
<h2 id="heading-coding-isnt-some-niche-skill-it-really-is-the-new-literacy">Coding isn’t Some Niche Skill. It really is “the new literacy.”</h2>
<p>It’s the essential 21st century skill that every ambitious person needs to learn if they want to succeed.</p>
<p>Don’t believe me? Just look at the legal profession. Software is turning it inside out, and <a target="_blank" href="http://www.abajournal.com/magazine/article/whos_eating_law_firms_lunch">causing mass unemployment</a> for the lawyers who can’t code.</p>
<p>The same is increasingly true for managers, marketers, accountants, doctors, and pretty much every white-collar job in between.</p>
<p>And that’s to say nothing of the 3 million Americans whose jobs primarily involve driving a car, and billions of people world-wide who do other repetitive tasks that will soon be handled more inexpensively and effectively by machines.</p>
<p>I’m hopeful that these displaced workers will be able to retrain for new jobs through inexpensive education programs like Starbuck’s partnership with Arizona State University — where all of its employees get a free college education (hopefully picking up relevant new skills like software development) — or government-sponsored equivalents.</p>
<p>At the very least, they’ll have access to a free math and computer science education through initiatives like EdX, and a free programming education through Free Code Camp.</p>
<h2 id="heading-program-or-be-programmed">Program or be Programmed</h2>
<p>We have a concept in software development called “the technology steamroller”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*UArvqBFDkaQGysgMEFhKjw.jpeg" alt="Image" width="500" height="390" loading="lazy">
<em>Stewart Brand, founder of the Whole Earth Catalog and the Long Now Foundation</em></p>
<blockquote>
<p>“Once a new technology rolls over you, if you’re not part of the steamroller, you’re part of the road.” — Stewart Brand</p>
</blockquote>
<p>You can’t stop technology. You can only adapt to it.</p>
<p>Once a history-shaping new technology comes out of the genie bottle, you can’t put it back. This was true for airplanes, antibiotics, and nuclear warheads. And it’s true for microprocessors, the internet, and machine learning.</p>
<p>Those who adapt to these permanent waves of changes flourish. Those who shrug them off — or fail to even realize they exist — asymptotically approach irrelevance.</p>
<p>Coding is the new literacy. Like reading was in the 12th century, writing was in the 16th century, arithmetic was in the 18th century, and driving a car was in the 20th century.</p>
<p>And just like how not everyone who learns to write will go on to become a professional writer — nor everyone who learns arithmetic will go on to become a professional mathematician — not everyone who learns to code will go on to become a software developer. But all people who learn these things will be immensely better off as a result of their efforts.</p>
<p>Think of your ability to read the labels on your prescription drugs, or your ability to count the money that a banker hands you when you make a withdrawal. There’s something equally important that you can do if you can code: take tedious parts of your daily life and automate them.</p>
<p>And some people take this basic skill much further, as a way to <a target="_blank" href="http://www.businessinsider.com/a-google-programmer-blew-off-a-500000-salary-at-startup--because-hes-already-making-3-million-every-year-2014-1">amass great personal wealth</a>, or to <a target="_blank" href="https://www.youtube.com/watch?v=G7UHzdvkd-Q&amp;index=3&amp;list=PLWKjhJtqVAbnQ048Pa8sAqJoVRhx8TJtM">make the world a better place</a>.</p>
<h2 id="heading-ships-are-meant-for-sailing">Ships are meant for sailing</h2>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*k0fUEL6Kuy9GMOgmWsKoBw.png" alt="Image" width="640" height="430" loading="lazy">
<em>Rear Admiral Grace Hopper invented the first compiler and pioneered high-level programming languages.</em></p>
<blockquote>
<p>A ship in port is safe, but that is not what ships are for. Sail out to sea and do new things. — Grace Hopper</p>
</blockquote>
<p>Computers, at their core, are number crunching machines.</p>
<p>Human brains, at their core, are learning machines.</p>
<p>It may seem like you’ll never be able to code. It may seem like you’re just not wired for it.</p>
<p>And there will probably be a parade of people behind you who’ve tried to learn to code, given up, and are eager to commiserate with you.</p>
<p>And these people will read articles like the Tech Crunch article, and share them on Facebook — like 14,000 people did yesterday — further discouraging the millions of people around the world who are working hard to achieve this new literacy.</p>
<p>But coding detractors are probably incorrect about their inability to learn coding. There’s a growing sentiment among educators and cognitive scientists that any able-minded person can learn to code — just like you can learn to read, write, do arithmetic, or drive a car.</p>
<p>Sure, people with dyslexia have a harder time reading, people with dyscalculia have a harder time doing math, and both have a harder time programming. But even these are limitations that can be overcome, and <a target="_blank" href="https://www.quora.com/Can-anyone-learn-how-to-code/answer/Quincy-Larson">programmers overcome limitations every day</a>.</p>
<p>So heed Grace Hopper’s advice. Sail out to sea and learn new things. Put that learning machine in your head to use.</p>
<p>Learn to code. Learn to talk to machines. And flourish.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ YouTube Subscribe Button: How to Get People to Subscribe to Your Channel From a Link ]]>
                </title>
                <description>
                    <![CDATA[ Did you know you can prompt people to subscribe when they visit your channel? Here is what this will look like to someone who clicks the link on a laptop or desktop computer: YouTube will show a Confirm channel subscription message. And don't worry ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/youtube-subscribe-button-link/</link>
                <guid isPermaLink="false">66b8d6cb6ebbe64e37d83822</guid>
                
                    <category>
                        <![CDATA[ how-to ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Mon, 05 Oct 2020 17:23:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/10/freeCodeCamp_org_-_YouTube-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Did you know you can prompt people to subscribe when they visit your channel?</p>
<p>Here is what this will look like to someone who clicks the link on a laptop or desktop computer:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/freeCodeCamp_org_-_YouTube.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>YouTube will show a Confirm channel subscription message.</em></p>
<p>And don't worry – if someone is already a subscriber to your channel when they click this link, they will just see your channel like normal, without the subscription confirmation message.</p>
<h2 id="heading-the-two-methods-of-getting-people-to-subscribe-to-your-youtube-channel-directly">The Two Methods of Getting People to Subscribe to Your YouTube Channel Directly</h2>
<p>There are two main methods you can use to accomplish this goal of getting people to subscribe to your channel directly:</p>
<ol>
<li>A YouTube Subscribe link you can use anywhere – including social media sites and messaging tools.</li>
<li>A YouTube Subscribe button you can use anywhere where you can embed JavaScript, such as your personal website.</li>
</ol>
<h2 id="heading-how-to-make-your-own-youtube-subscribe-link">How to Make Your Own YouTube Subscribe Link</h2>
<p>YouTube has a feature where you can just add the <code>?sub_confirmation=1</code> parameter to your YouTube channel URL.</p>
<p>Again, this is perfect for linking to your YouTube channel from social media or another place where you don't have the ability to insert code for a proper subscribe button.</p>
<p>There are two types of channels on YouTube:</p>
<ol>
<li>Channel Channels</li>
<li>Users Channels</li>
</ol>
<p>In practice, there is no major difference between these types of channels. They each just use a slightly different URL structure.</p>
<h3 id="heading-how-to-create-a-subscribe-link-if-your-youtube-channel-is-classified-as-a-channel">How to Create a Subscribe Link if Your YouTube Channel is Classified as a Channel</h3>
<p>You can tell your channel uses the "channel" structure by visiting your channel and seeing whether it has the word "channel" in the address bar.</p>
<p>Here's an example:</p>
<pre><code>https:<span class="hljs-comment">//www.youtube.com/channel/freecodecamp</span>
</code></pre><p>See the word "channel" here? So in this case, you can use this structure for your link:</p>
<pre><code>https:<span class="hljs-comment">//www.youtube.com/channel/&lt;YOUR CHANNEL ID&gt;?sub_confirmation=1</span>
</code></pre><p>You would just replace the <code>&lt;YOUR CHANNEL ID&gt;</code> in this URL with your channel's ID, which you can find by going to your YouTube channel. </p>
<p>It will either be a custom name (in this case, <code>freecodecamp</code>) or it will be a string of base-64 characters like this: <code>UC0syIz79dzjMXIf5VdJ65EA</code></p>
<p>Once you add your channel ID to that link, you'll be good to go. The people who click that link will not only be taken to your channel, but they'll also see the subscription confirmation prompt.</p>
<h3 id="heading-how-to-create-a-subscribe-link-if-your-youtube-channel-is-classified-as-a-user">How to Create a Subscribe Link if Your YouTube Channel is Classified as a User</h3>
<p>Some older channels are still set up as users rather than channels. You can tell your channel uses the "user" structure by visiting your channel and seeing whether it has the word "user" in the address bar.</p>
<p>Here's an example:</p>
<pre><code>https:<span class="hljs-comment">//www.youtube.com/user/thenewboston</span>
</code></pre><p>This channel is set up as a user.</p>
<p>In this case, you would use this structure:</p>
<pre><code>https:<span class="hljs-comment">//www.youtube.com/user/&lt;YOUR CHANNEL ID&gt;?sub_confirmation=1</span>
</code></pre><p>You would just replace the <code>&lt;YOUR CHANNEL ID&gt;</code> in this URL with your channel's ID. It will either be a custom name (in this case, <code>thenewboston</code>) or it will be a string of base-64 characters like this: <a target="_blank" href="https://www.youtube.com/channel/UC0syIz79dzjMXIf5VdJ65EA"><code>UC0syIz79dzjMXIf5VdJ65EA</code></a></p>
<p>Once you add your channel ID to that link, you'll be good to go.</p>
<h2 id="heading-how-to-make-your-own-youtube-subscribe-button">How to Make Your Own YouTube Subscribe Button</h2>
<p>All right – here is the fun part. YouTube gives you a way to embed subscribe buttons directly into your website.</p>
<p>Here is what one of these buttons looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Configure_a_Button_-_-_YouTube_Subscribe_Button_-_-_Google_Developers.png" alt="Image" width="600" height="400" loading="lazy">
_This is a static image that leads to <a target="_blank" href="https://www.youtube.com/c/freecodecamp?sub_confirmation=1">a subscribe prompt</a>._</p>
<p>And here's the embed-able HTML code you would add to your blog. Note that this code will import Google's <code>platform.js</code> JavaScript library in order to dynamically show the button and your current subscriber count.</p>
<pre><code>&lt;script src=<span class="hljs-string">"https://apis.google.com/js/platform.js"</span>&gt;&lt;/script&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"g-ytsubscribe"</span> <span class="hljs-attr">data-channelid</span>=<span class="hljs-string">"&lt;YOUR CHANNEL ID&gt;"</span> <span class="hljs-attr">data-layout</span>=<span class="hljs-string">"full"</span> <span class="hljs-attr">data-theme</span>=<span class="hljs-string">"dark"</span> <span class="hljs-attr">data-count</span>=<span class="hljs-string">"default"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre><p>You can embed this code. Be sure to replace <code>&lt;YOUR CHANNEL ID&gt;</code> with the channel ID you see when you visit your page. </p>
<p>If you have a custom YouTube channel URL like <code>https://www.youtube.com/freecodecamp</code> you may be able to use that as your channel ID, but I find it more reliable to use the full channel 24-character ID.</p>
<h2 id="heading-how-to-customize-your-youtube-subscribe-button">How to Customize Your YouTube Subscribe Button</h2>
<p>There are two other ways you can customize your subscribe button.</p>
<h3 id="heading-how-to-show-your-channel-name-and-logo-in-your-subscribe-button">How to Show Your Channel Name and Logo in your Subscribe Button</h3>
<p>You can change <code>data-layout</code> to be either <code>default</code> or <code>full</code> (which will show your channel name and icon).</p>
<p>Here is what this looks like when you set <code>data-layout="default"</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Configure_a_Button_-_-_YouTube_Subscribe_Button_-_-_Google_Developers-1.png" alt="Image" width="600" height="400" loading="lazy">
_This is a static image that would lead to <a target="_blank" href="https://www.youtube.com/c/freecodecamp?sub_confirmation=1">a subscribe prompt</a>._</p>
<p>And here's what this looks like when you set <code>data-layout="full"</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Configure_a_Button_-_-_YouTube_Subscribe_Button_-_-_Google_Developers.png" alt="Image" width="600" height="400" loading="lazy">
_This is a static image that leads to <a target="_blank" href="https://www.youtube.com/c/freecodecamp?sub_confirmation=1">a subscribe prompt</a>._</p>
<p>You can also set the theme to dark with <code>data-theme="dark"</code>.</p>
<p>And you can hide your subscriber count completely with <code>data-count="hidden"</code>. If you only have a handful of subscribers, you may want to hide this for a few months while you build up a thousand subscribers or more, to avoid "negative social proof".</p>
<h1 id="heading-why-i-recommend-youtube-subscribe-links-instead-of-youtube-subscribe-buttons">Why I Recommend YouTube Subscribe Links Instead of YouTube Subscribe Buttons</h1>
<p>There are several reasons why I recommend using the link approach instead of these dynamic buttons.</p>
<ol>
<li>Ad blockers, firewalls, and browser plugins may block the button from rendering correctly or from working correctly. This button does involve pulling a JavaScript file from Google's CDNs, which means it won't render in China, for example, where Google is currently blocked.</li>
<li>It is hard to control the styling of these buttons, and they may end up looking bad on a mobile device.</li>
<li>These buttons may lead to accessibility issues. The link, on the other hand, is just a link, and is easy for people to use in screen readers or other assistive tools.</li>
</ol>
<p>But Google does support these YouTube subscription buttons as well, so it's up to you whether you want to use them.</p>
<h2 id="heading-a-youtube-subscribe-button-customization-tool">A YouTube Subscribe Button Customization Tool</h2>
<p>Google has an official tool for customizing these YouTube subscribe buttons. <a target="_blank" href="https://developers.google.com/youtube/youtube_subscribe_button">You can access it here</a>. Note that you will still need to have access to the HTML of the page you want to embed these buttons into.</p>
<p>Thanks for reading this guide, and I hope it has helped you understand how these YouTube subscribe links and buttons work, and how you can use them to get more people to subscribe to your channel.</p>
<p>If you want more tips on being a successful YouTube creator in general, you can learn from our nonprofit's 5+ years of experimentation that has helped us become the largest programming channel on YouTube. </p>
<p>Here's our <a target="_blank" href="https://www.freecodecamp.org/news/how-to-start-a-software-youtube-channel/">free YouTube handbook, which also includes a 1 hour video course</a>. We designed it with software-focused creators in mind, but many of the techniques can be applied to other subject domains. I hope it's helpful for you.</p>
<p>Cheers.</p>
<p>– Quincy</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Bot and Automate your Everyday Work ]]>
                </title>
                <description>
                    <![CDATA[ By Tim Grossmann Most jobs have repetitive tasks that you can automate, which frees up some of your valuable time. This makes automation a key skill to acquire. A small group of skilled automation engineers and domain experts may be able to automate ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-bots/</link>
                <guid isPermaLink="false">66d4614bffe6b1f641b5fa93</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 21 Jul 2020 18:36:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/freecodecamp_cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Tim Grossmann</p>
<p>Most jobs have repetitive tasks that you can automate, which frees up some of your valuable time. This makes automation a key skill to acquire.</p>
<p>A small group of skilled automation engineers and domain experts may be able to automate many of the most tedious tasks of entire teams.</p>
<p>In this article, we'll explore the basics of workflow automation using Python – a powerful and easy to learn programming language. We will use Python to write an easy and helpful little automation script that will clean up a given folder and put each file into its according folder. </p>
<p>Our goal won't be to write perfect code or create ideal architectures in the beginning.<br>We also won't build anything "illegal". Instead we'll look at how to create a script that automatically cleans up a given folder and all of its files. </p>
<h1 id="heading-table-of-contents">Table of contents</h1>
<ol>
<li><a class="post-section-overview" href="#heading-areas-of-automation-and-where-to-start">Areas of Automation and Where to Start</a> <ul>
<li>Simple Automation</li>
<li>Public API Automation</li>
<li>API Reverse Engineering</li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-ethical-considerations">Ethical Considerations of Automation</a></li>
<li><a class="post-section-overview" href="#heading-creating-a-directory-clean-up-script">Creating a Directory Clean-Up Script</a></li>
<li><a class="post-section-overview" href="#heading-a-complete-guide-to-bot-creation-and-automating-your-everyday-work">A Complete Guide to Bot Creation and Automating Your Everyday Work</a></li>
</ol>
<h2 id="heading-areas-of-automation-and-where-to-start">Areas of Automation and Where to Start</h2>
<p>Let's start with defining what kind of automations there are.</p>
<p>The art of automation applies to most sectors. For starters, it helps with tasks like extracting email addresses from a bunch of documents so you can do an email blast. Or more complex approaches like optimizing workflows and processes inside of large corporations.</p>
<p>Of course, going from small personal scripts to large automation infrastructure that replaces actual people involves a process of learning and improving. So let's see where you can start your journey.</p>
<h3 id="heading-simple-automations">Simple Automations</h3>
<p>Simple automations allow for a quick and straightforward entry point. This can cover small independent processes like project clean-ups and re-structuring of files inside of directories, or parts of a workflow like automatically resizing already saved files. </p>
<h3 id="heading-public-api-automations">Public API Automations</h3>
<p>Public API automations are the most common form of automation since we can access most functionality using HTTP requests to APIs nowadays. For example, if you want to automate the watering of your self-made smart garden at home.</p>
<p>To do that, you want to check the weather of the current day to see whether you need to water or if there is rain incoming.</p>
<h3 id="heading-api-reverse-engineering">API Reverse Engineering</h3>
<p>API reverse engineering-based automation is more common in actual bots and the "Bot Imposter" section of the chart in the "Ethical Considerations" section below. </p>
<p>By reverse-engineering an API, we understand the user flow of applications. One example could be the login into an online browser game. </p>
<p>By understanding the login and authentication process, we can duplicate that behaviour with our own script. Then we can create our own interface to work with the application even though they don't provide it themselves.</p>
<p>Whatever approach you're aiming at, always consider whether it's legal or not.</p>
<p>You don't want to get yourself into trouble, do you? ?</p>
<h2 id="heading-ethical-considerations">Ethical Considerations</h2>
<p>Some guy on GitHub once contacted me and told me this:</p>
<blockquote>
<p>“Likes and engagement are digital currency and you are devaluing them.”</p>
</blockquote>
<p>This stuck with me and made me question the tool I've built for exactly that purpose.</p>
<p>The fact that these interactions and the engagement can be automated and “faked” more and more leads to a distorted and broken social media system.</p>
<p>People who produce valuable and good content are invisible to other users and advertisement companies if they don’t use bots and other engagement systems.  </p>
<p>A friend of mine came up with the following association with Dante’s “Nine Circles of Hell” where with each step closer to becoming a social influencer you get less and less aware of how broken this whole system actually is. </p>
<p>I want to share this with you here since I think it's an extremely accurate representation of what I witnessed while actively working with Influencers with InstaPy.</p>
<p><strong>Level 1: Limbo -</strong> If you don’t bot at all<br><strong>Level 2: Flirtation</strong> - When you manually like and follow as many people as you can to get them to follow you back / like your posts<br><strong>Level 3: Conspiracy</strong> - when you join a Telegram group to like and comment on 10 photos so the next 10 people will like and comment on your photo<br><strong>Level 4: Infidelity</strong> - When you use a low-cost Virtual Assistant to like and follow on your behalf<br><strong>Level 5: Lust -</strong> When you use a bot to give likes, and don’t receive any likes back in return (but you don’t pay for it - for example, a Chrome extension)<br><strong>Level 6: Promiscuity -</strong> When you use a bot to Give 50+ likes to Get 50+ likes, but you don’t pay for it - for example, a Chrome extension<br><strong>Level 7: Avarice or Extreme Greed</strong> - When you use a bot to Like / Follow / Comment on between 200–700 photos, ignoring the chance of getting banned<br><strong>Level 8: Prostitution</strong> - When you pay an unknown 3rd party service to engage in automated reciprocal likes / follows for you, but they use your account to like / follow back<br><strong>Level 9: Fraud / Heresy</strong> - When you buy followers and likes and try to sell yourself to brands as an influencer</p>
<p>The level of botting on social media is so prevalent that <strong>if you don’t bot, you will be stuck in Level 1, Limbo</strong>, with no follower growth and low engagement relative to your peers.</p>
<p>In economic theory, this is known as a <strong>prisoner's dilemma and zero-sum game</strong>. If I don’t bot and you bot, you win. If you don’t bot and I bot, I win. If no one bots, everyone wins. But since there is no incentive for everyone not to bot, everyone bots, so no one wins.</p>
<blockquote>
<p>Be aware of this and never forget the implications this whole tooling has on social media.</p>
</blockquote>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/spectrum-bot-intent-ebook.png" alt="Image" width="600" height="400" loading="lazy">
<em>Source: SignalSciences.com</em></p>
<p>We want to avoid dealing with ethical implications and still work on an automation project here. This is why we will create a simple directory clean-up script that helps you organise your messy folders. </p>
<h2 id="heading-creating-a-directory-clean-up-script">Creating a Directory Clean-Up Script</h2>
<p>We now want to look at a quite simple script. It automatically cleans up a given directory by moving those files into according folders based on the file extension.</p>
<p>So all we want to do is this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/directory_clean_img.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-setting-up-the-argument-parser">Setting up the Argument Parser</h3>
<p>Since we are working with operating system functionality like moving files, we need to import the <code>os</code> library. In addition to that, we want to give the user some control over what folder is cleaned up. We will use the <code>argparse</code> library for this.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> argparse
</code></pre>
<p>After importing the two libraries, let's first set up the argument parser. Make sure to give a description and a help text to each added argument to give valuable help to the user when they type <code>--help</code>.</p>
<p>Our argument will be named <code>--path</code>. The double dashes in front of the name tell the library that this is an optional argument. By default we want to use the current directory, so set the default value to be <code>"."</code>.</p>
<pre><code class="lang-python">parser = argparse.ArgumentParser(
    description=<span class="hljs-string">"Clean up directory and put files into according folders."</span>
)

parser.add_argument(
    <span class="hljs-string">"--path"</span>,
    type=str,
    default=<span class="hljs-string">"."</span>,
    help=<span class="hljs-string">"Directory path of the to be cleaned directory"</span>,
)

<span class="hljs-comment"># parse the arguments given by the user and extract the path</span>
args = parser.parse_args()
path = args.path

print(<span class="hljs-string">f"Cleaning up directory <span class="hljs-subst">{path}</span>"</span>)
</code></pre>
<p>This already finishes the argument parsing section – it's quite simple and readable, right?</p>
<p>Let's execute our script and check for errors.</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; Cleaning up directory ./<span class="hljs-built_in">test</span>
</code></pre>
<p>Once executed, we can see the directory name being printed to the console, perfect.<br>Let's now use the <code>os</code> library to get the files of the given path. </p>
<h3 id="heading-getting-a-list-of-files-from-the-folder">Getting a list of files from the folder</h3>
<p>By using the <code>os.listdir(path)</code> method and providing it a valid path, we get a list of all the files and folders inside of that directory.</p>
<p>After listing all elements in the folder, we want to differentiate between files and folders since we don't want to clean up the folders, only the files.</p>
<p>In this case, we use a Python list comprehension to iterate through all the elements and put them into the new lists if they meet the given requirement of being a file or folder.</p>
<pre><code class="lang-python"><span class="hljs-comment"># get all files from given directory</span>
dir_content = os.listdir(path)

<span class="hljs-comment"># create a relative path from the path to the file and the document name</span>
path_dir_content = [os.path.join(path, doc) <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> dir_content]

<span class="hljs-comment"># filter our directory content into a documents and folders list</span>
docs = [doc <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> path_dir_content <span class="hljs-keyword">if</span> os.path.isfile(doc)]
folders = [folder <span class="hljs-keyword">for</span> folder <span class="hljs-keyword">in</span> path_dir_content <span class="hljs-keyword">if</span> os.path.isdir(folder)]

<span class="hljs-comment"># counter to keep track of amount of moved files </span>
<span class="hljs-comment"># and list of already created folders to avoid multiple creations</span>
moved = <span class="hljs-number">0</span>
created_folders = []

print(<span class="hljs-string">f"Cleaning up <span class="hljs-subst">{len(docs)}</span> of <span class="hljs-subst">{len(dir_content)}</span> elements."</span>)
</code></pre>
<p>As always, let's make sure that our users get feedback. So add a print statement that gives the user an indication about how many files will be moved.</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; Cleaning up directory ./<span class="hljs-built_in">test</span>
=&gt; Cleaning up 60 of 60 elements.
</code></pre>
<p>After re-executing the python script, we can now see that the <code>/test</code> folder I created contains 60 files that will be moved.</p>
<h3 id="heading-creating-a-folder-for-every-file-extension">Creating a folder for every file extension</h3>
<p>The next and more important step now is to create the folder for each of the file extensions. We want to do this by going through all of our filtered files and if they have an extension for which there is no folder already, create one.</p>
<p>The <code>os</code> library helps us with more nice functionality like the splitting of the filetype and path of a given document, extracting the path itself and name of the document.  </p>
<pre><code class="lang-python"><span class="hljs-comment"># go through all files and move them into according folders</span>
<span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> docs:
    <span class="hljs-comment"># separte name from file extension</span>
    full_doc_path, filetype = os.path.splitext(doc)
    doc_path = os.path.dirname(full_doc_path)
    doc_name = os.path.basename(full_doc_path)

    print(filetype)
    print(full_doc_path)
    print(doc_path)
    print(doc_name)

    <span class="hljs-keyword">break</span>
</code></pre>
<p>The break statement at the end of the code above makes sure that our terminal does not get spammed if our directory contains dozens of files.</p>
<p>Once we've set this up, let's execute our script to see an output similar to this:</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; ...
=&gt; .pdf
=&gt; ./<span class="hljs-built_in">test</span>/test17
=&gt; ./<span class="hljs-built_in">test</span>
=&gt; test17
</code></pre>
<p>We can now see that the implementation above splits off the filetype and then extracts the parts from the full path.</p>
<p>Since we have the filetype now, we can check if a folder with the name of this type already exists. </p>
<p>Before we do that, we want to make sure to skip a few files. If we use the current directory <code>"."</code> as the path, we need to avoid moving the python script itself. A simple if condition takes care of that. </p>
<p>In addition to that, we don't want to move <a target="_blank" href="https://www.lifewire.com/what-is-a-hidden-file-2625898">Hidden Files</a>, so let's also include all files that start with a dot. The <code>.DS_Store</code> file on macOS is an example of a hidden file.</p>
<pre><code class="lang-python">    <span class="hljs-comment"># skip this file when it is in the directory</span>
    <span class="hljs-keyword">if</span> doc_name == <span class="hljs-string">"directory_clean"</span> <span class="hljs-keyword">or</span> doc_name.startswith(<span class="hljs-string">'.'</span>):
        <span class="hljs-keyword">continue</span>

    <span class="hljs-comment"># get the subfolder name and create folder if not exist</span>
    subfolder_path = os.path.join(path, filetype[<span class="hljs-number">1</span>:].lower())

    <span class="hljs-keyword">if</span> subfolder_path <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> folders:
        <span class="hljs-comment"># create the folder</span>
</code></pre>
<p>Once we've taken care of the python script and hidden files, we can now move on to creating the folders on the system.</p>
<p>In addition to our check, if the folder already was there when we read the content of the directory, in the beginning, we need a way to track the folders we've already created. That was the reason we declared the <code>created_folders = []</code> list. It will serve as the memory to track the names of folders.</p>
<p>To create a new folder, the <code>os</code> library provides a method called <code>os.mkdir(folder_path)</code> that takes a path and creates a folder with the given name there. </p>
<p>This method may throw an exception, telling us that the folder already exists. So let's also make sure to catch that error.</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> subfolder_path <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> folders <span class="hljs-keyword">and</span> subfolder_path <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> created_folders:
        <span class="hljs-keyword">try</span>:
            os.mkdir(subfolder_path)
            created_folders.append(subfolder_path)
            print(<span class="hljs-string">f"Folder <span class="hljs-subst">{subfolder_path}</span> created."</span>)
        <span class="hljs-keyword">except</span> FileExistsError <span class="hljs-keyword">as</span> err:
            print(<span class="hljs-string">f"Folder already exists at <span class="hljs-subst">{subfolder_path}</span>... <span class="hljs-subst">{err}</span>"</span>)
</code></pre>
<p>After setting up the folder creation, let's re-execute our script.</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; ...
=&gt; Folder ./<span class="hljs-built_in">test</span>/pdf created.
</code></pre>
<p>On the first run of execution, we can see a list of logs telling us that the folders with the given types of file extensions have been created.</p>
<h3 id="heading-moving-each-file-into-the-right-subfolder">Moving each file into the right subfolder</h3>
<p>The last step now is to actually move the files into their new parent folders.</p>
<p>An important thing to understand when working with os operations is that sometimes operations can not be undone. This is, for example, the case with deletion. So it makes sense to first only log out the behavior our script would achieve if we execute it.</p>
<p>This is why the <code>os.rename(...)</code> method has been commented here.</p>
<pre><code class="lang-python"><span class="hljs-comment"># get the new folder path and move the file</span>
    new_doc_path = os.path.join(subfolder_path, doc_name) + filetype
    <span class="hljs-comment"># os.rename(doc, new_doc_path)</span>
    moved += <span class="hljs-number">1</span>

    print(<span class="hljs-string">f"Moved file <span class="hljs-subst">{doc}</span> to <span class="hljs-subst">{new_doc_path}</span>"</span>)
</code></pre>
<p>After executing our script and seeing the correct logging, we can now remove the comment hash before our <code>os.rename()</code> method and give it a final go.</p>
<pre><code class="lang-python"><span class="hljs-comment"># get the new folder path and move the file</span>
    new_doc_path = os.path.join(subfolder_path, doc_name) + filetype
    os.rename(doc, new_doc_path)
    moved += <span class="hljs-number">1</span>

    print(<span class="hljs-string">f"Moved file <span class="hljs-subst">{doc}</span> to <span class="hljs-subst">{new_doc_path}</span>"</span>)

print(<span class="hljs-string">f"Renamed <span class="hljs-subst">{moved}</span> of <span class="hljs-subst">{len(docs)}</span> files."</span>)
</code></pre>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; ...
=&gt; Moved file ./<span class="hljs-built_in">test</span>/test17.pdf to ./<span class="hljs-built_in">test</span>/pdf/test17.pdf
=&gt; ...
=&gt; Renamed 60 of 60 files.
</code></pre>
<p>This final execution will now move all the files into their appropriate folders and our directory will be nicely cleaned up without the need for manual actions.</p>
<p>In the next step, we could now use the script we created above and, for example, schedule it to execute every Monday to clean up our Downloads folder for more structure.</p>
<p><strong>That is exactly what we are creating as a follow-up inside of <a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">our Bot Creation and Workflow Automation Udemy course</a>.</strong></p>
<h2 id="heading-a-complete-guide-to-bot-creation-and-automating-your-everyday-workhttpswwwudemycomcoursethe-complete-guide-to-bot-creation"><a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">A Complete Guide to Bot Creation and Automating Your Everyday Work</a></h2>
<p>Felix and I built an <strong>online video course to teach you how to create your own bots</strong> based on what we've learned building <strong>InstaPy</strong> and his <strong>Travian-Bot</strong>. In fact, he <strong>was even forced to take down since it was too effective.</strong></p>
<h3 id="heading-join-right-in-and-start-learninghttpswwwudemycomcoursethe-complete-guide-to-bot-creation"><a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">Join right in and start learning</a>.</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/zw20WBPjsr0" 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>If you have any questions or feedback, feel free to reach out to us on <a target="_blank" href="https://twitter.com/timigrossmann">Twitter</a> or directly in the <a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">discussion section of the course</a> ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Open Graph and how can I use it for my website? ]]>
                </title>
                <description>
                    <![CDATA[ It can take a lot of time to build content and maintain a website. How can we make sure our content stands out when getting shared on social feeds around the internet? What is Open Graph? Why do I need it? What happens if I don’t have it? Starting w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-open-graph-and-how-can-i-use-it-for-my-website/</link>
                <guid isPermaLink="false">66b8e39ac9bc6d235bb126b4</guid>
                
                    <category>
                        <![CDATA[ #content marketing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Digital Marketing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ marketing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ open graph ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Thu, 26 Mar 2020 14:51:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/open-graph.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>It can take a lot of time to build content and maintain a website. How can we make sure our content stands out when getting shared on social feeds around the internet?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-open-graph">What is Open Graph?</a></li>
<li><a class="post-section-overview" href="#heading-why-do-i-need-it">Why do I need it?</a></li>
<li><a class="post-section-overview" href="#heading-what-happens-if-i-dont-have-it">What happens if I don’t have it?</a></li>
<li><a class="post-section-overview" href="#heading-starting-with-the-basics-of-open-graph">Starting with the basics of open graph</a></li>
<li><a class="post-section-overview" href="#heading-website-open-graph-type">Website open graph type</a></li>
<li><a class="post-section-overview" href="#heading-some-other-open-graph-tags-that-are-worth-adding">Some other open graph tags that are worth adding</a></li>
<li><a class="post-section-overview" href="#heading-twitter-and-other-social-media-networks-using-open-graph">Twitter and other social media networks using open graph</a></li>
<li><a class="post-section-overview" href="#heading-images-in-open-graph">Images in open graph</a></li>
<li><a class="post-section-overview" href="#heading-testing-your-open-graph-tags">Testing your open graph tags</a></li>
<li><a class="post-section-overview" href="#heading-can-i-get-an-example">Can I get an example?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/QwEQKM4YRnU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-open-graph">What is Open Graph?</h2>
<p><a target="_blank" href="https://ogp.me/">Open Graph</a> is an internet protocol that was originally created by <a target="_blank" href="http://fbdevwiki.com/wiki/Open_Graph_protocol">Facebook</a> to standardize the use of metadata within a webpage to represent the content of a page.</p>
<p>Within it, you can provide details as simple as the title of a page or as specific as the duration of a video. These pieces all fit together to form a representation of each individual page of the internet.</p>
<h2 id="heading-why-do-i-need-it">Why do I need it?</h2>
<p>Content on the internet is typically created with at least one goal in mind -- to share it with others. This might not necessarily matter if you’re just sending it to one friend, but if you want to share it or want it to be shared on any social network or app that utilizes rich previews, you’ll want that preview to be as effective as possible.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1237455806230077441"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>This will help encourage people to check out your content and inevitably click through to your content.</p>
<h2 id="heading-what-happens-if-i-dont-have-it">What happens if I don’t have it?</h2>
<p>Most social networks by default will try to make their best effort in creating a preview of your content. This more often than not doesn’t go so well.</p>
<p>Take for instance my website <a target="_blank" href="https://colbyfayock.com">colbyfayock.com</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/simple-twitter-card.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Example of a simple Twitter Card</em></p>
<p>It correctly grabs the title of my page and the description, but it's not the most enticing looking tweet in a feed.</p>
<p>Contrast that to the preview of a single post and we see a different story.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/large-image-twitter-card.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Example of a Twitter Card with a large image</em></p>
<p>So what happens if you don’t have open graph tags? Nothing bad will happen, but you won’t be taking advantage of some of the features that help make your content stand out next to the loads of other content getting posted on the internet.</p>
<h2 id="heading-starting-with-the-basics-of-open-graph">Starting with the basics of open graph</h2>
<p>The four basic open graph tags that are required for each page are <code>og:title</code>, <code>og:type</code>, <code>og:image</code>, and <code>og:url</code>. These tags should be unique for each page you serve, meaning your homepage’s tags should all be different from your blog post article’s page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/open-graph-twitter-card.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Anatomy of a Twitter Card using Open Graph tags</em></p>
<p>While it should be pretty straightforward, here’s a breakdown of what each of the tags mean:</p>
<ul>
<li><code>og:title</code>: The title of your page. This is typically the same as your webpage's <code>&lt;title&gt;</code> tag unless you’d like to present it differently.</li>
<li><code>og:type</code>: The “type” of website you have. I’ll explain more in the next section, though a generic “type” is “website”.</li>
<li><code>og:image</code>: This should be a link to an image that you’d like to represent your content. It should be a high resolution image that the social networks will use in their feeds.</li>
<li><code>og:url</code>: This should be the URL of the current page.</li>
</ul>
<p>When placing a tag on your website, you should place it in the <code>&lt;head&gt;</code> along with any other metadata. The tag used will be a <code>&lt;meta&gt;</code> tag and should look like this pattern:</p>
<pre><code>&lt;meta property=“[NAME]” content=“[VALUE]” /&gt;
</code></pre><p>So if I were to create a set four basic open graph tags for my website, <a target="_blank" href="https://colbyfayock.com">colbyfayock.com</a>, it might look like:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:title"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Colby Fayock - A UX Designer <span class="hljs-symbol">&amp;amp;</span> Front-end Developer Blog"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:type"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"website"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"/static/website-social-card-44070c4a901df708aa1563ac4bbe595a.jpg"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:url"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://www.colbyfayock.com"</span> /&gt;</span>
</code></pre>
<h2 id="heading-website-open-graph-type">Website open graph type</h2>
<p>The open graph protocol has a few variations of the “type” of website it supports. This includes types like website, article, or video.</p>
<p>When setting up your open graph tags, you’ll want to have an idea of which type will make more sense for your website. If your page is focused on a single video, it probably makes sense to use the type “video”. If it’s a general website with no specific vertical, you would probably just want to use the type “website”.</p>
<p>Similar to the others, this is unique for each page. So if your homepage is "website,” you could always have another page of type “video”.</p>
<p>So if I were to create an open graph type for my website, it might look like the following on my homepage:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- colbyfayock.com --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">“og:type”</span> <span class="hljs-attr">content</span>=<span class="hljs-string">“profile”</span> /&gt;</span>
</code></pre>
<p>When navigating to a blog post, it would look like:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping/ --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">“og:type”</span> <span class="hljs-attr">content</span>=<span class="hljs-string">“article”</span> /&gt;</span>
</code></pre>
<p>You can find the most common open graph website types on the open graph webpage: <a target="_blank" href="https://ogp.me/#types">https://ogp.me/#types</a></p>
<h2 id="heading-some-other-open-graph-tags-that-are-worth-adding">Some other open graph tags that are worth adding</h2>
<p>Though you’ll generally be okay with the basics, here are a few more that would be worth adding:</p>
<ul>
<li><code>og:description</code>: A description of your page. Similarly to <code>og:title</code>, this may be the same as your website’s <code>&lt;meta type=“description”&gt;</code> tag, unless you’d like to present it differently.</li>
<li><code>og:locale</code>: If you want to localize your tags, it would probably make sense to include locale. The format is <code>language_TERRITORY</code>, where the default is <code>en_US</code>.</li>
<li><code>og:site_name</code>: The name of the overall website your content is on. If you're on a blog post page, you might have a <code>title</code> using that blog post’s title, where the <code>site_name</code> would be the name of your blog.</li>
<li><code>og:video</code>: Have a video that supports your content? Here’s a chance to include it. Add a link to your video using this tag.</li>
</ul>
<p>These tags will be added in the same pattern as before:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">“[NAME]”</span> <span class="hljs-attr">content</span>=<span class="hljs-string">“[VALUE]”</span> /&gt;</span>
</code></pre>
<h2 id="heading-twitter-and-other-social-media-networks-using-open-graph">Twitter and other social media networks using open graph</h2>
<p>Most of the social networks adhere to the basics of open graph standards, but a few of them also include their own extension to help customize the look and feel within their ecosystem.</p>
<p>Twitter for instance, allows you to specify <code>twitter:card</code>, which is the type of “card” you can use when they show your website. At this time, their card types include:</p>
<ul>
<li>summary</li>
<li>summary_large_image</li>
<li>app</li>
<li>player</li>
</ul>
<p>This will help you choose how your links are used in their feed. If you choose <code>summary_large_image</code> for instance, Twitter will show your links with big high resolution images as long as you’re providing it in the in the <code>og:image</code> tag.</p>
<p>Here are some quick references to the documentation of how to use open graph tags with some of the social media sites:</p>
<ul>
<li>Twitter: <a target="_blank" href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started">https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started</a></li>
<li>Facebook: <a target="_blank" href="https://developers.facebook.com/docs/sharing/webmasters/">https://developers.facebook.com/docs/sharing/webmasters/</a></li>
<li>Pinterest: <a target="_blank" href="https://developers.pinterest.com/docs/rich-pins/overview/">https://developers.pinterest.com/docs/rich-pins/overview/</a>?</li>
<li>LinkedIn: <a target="_blank" href="https://www.linkedin.com/help/linkedin/answer/46687/making-your-website-shareable-on-linkedin?lang=en">https://www.linkedin.com/help/linkedin/answer/46687/making-your-website-shareable-on-linkedin?lang=en</a></li>
</ul>
<h2 id="heading-images-in-open-graph">Images in open graph</h2>
<p>While adding your image as <code>og:image</code> should often be enough, sometimes it can be challenging to get your image to show up correctly. If you seem to be running into trouble, the open graph standard includes a few image tags such as <code>og:image:url</code> vs <code>og:image:secure_url</code> as well as the <code>og:image:width</code> and <code>og:image:height</code>.</p>
<p>Try to make sure you’re following all of the <a target="_blank" href="https://ogp.me/#structured">notes and examples in the open graph documentation</a>. Additionally, some of the social networks have image requirements. <a target="_blank" href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image">Twitter for instance requires</a> a ratio of 2:1 with a minimum size of 300x157 and a maximum size of 4096x4096 that’s under 5MB and of JPG, PNG, WEBP or GIF format.</p>
<p>If you’re stuck, test your tags using the social media network’s tools to see if you can find the issue.</p>
<h2 id="heading-testing-your-open-graph-tags">Testing your open graph tags</h2>
<p>Luckily, our favorite social networks also provide tools to help us debug our tags. Once you make sure that your tags are actually showing up in the source code of your website, you’ll be able to preview how your website will look in the feed.</p>
<ul>
<li>Twitter: <a target="_blank" href="https://cards-dev.twitter.com/validator">https://cards-dev.twitter.com/validator</a></li>
<li>Facebook: <a target="_blank" href="https://developers.facebook.com/tools/debug/">https://developers.facebook.com/tools/debug/</a></li>
<li>Pinterest: <a target="_blank" href="https://developers.pinterest.com/tools/url-debugger/">https://developers.pinterest.com/tools/url-debugger/</a></li>
</ul>
<h2 id="heading-digging-further-into-open-graph-tags">Digging further into open graph tags</h2>
<p>While most of these should cover a basic website, there are a few more tags that might help you and your business’s discoverability throughout social networks. </p>
<p>If you’re interested in diving in more, <a target="_blank" href="https://ogp.me/">the documentation</a> does a great job at providing a list of all of the available tags for you to use.</p>
<p><a target="_blank" href="https://ogp.me/">https://ogp.me/</a></p>
<h2 id="heading-can-i-get-an-example">Can I get an example?</h2>
<p>If you’re simply looking for an example to get started, here’s what you should end up with when setting up your tags for <a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping/">a blog post</a>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:site_name"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Colby Fayock"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">“og:title”</span> <span class="hljs-attr">content</span>=<span class="hljs-string">“Anyone</span> <span class="hljs-attr">Can</span> <span class="hljs-attr">Map</span>! <span class="hljs-attr">Inspiration</span> <span class="hljs-attr">and</span> <span class="hljs-attr">an</span> <span class="hljs-attr">introduction</span> <span class="hljs-attr">to</span> <span class="hljs-attr">the</span> <span class="hljs-attr">world</span> <span class="hljs-attr">of</span> <span class="hljs-attr">mapping</span> <span class="hljs-attr">-</span> <span class="hljs-attr">Colby</span> <span class="hljs-attr">Fayock</span>" /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Chef Gusteau was a visionary who created food experiences for the world to enjoy. How can we take his lessons and apply them to the world of…"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:url"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping/"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:type"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"article"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"article:publisher"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://www.colbyfayock.com"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"article:section"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Coding"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"article:tag"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Coding"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://res.cloudinary.com/fay/image/upload/w_1280,h_640,c_fill,q_auto,f_auto/w_860,c_fit,co_rgb:232129,g_west,x_80,y_-60,l_text:Source%20Sans%20Pro_70_line_spacing_-10_semibold:Anyone%20Can%20Map!%20Inspiration%20and%20an%20introduction%20to%20the%20world%20of%20mapping/blog-social-card-1.1"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image:secure_url"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://res.cloudinary.com/fay/image/upload/w_1280,h_640,c_fill,q_auto,f_auto/w_860,c_fit,co_rgb:232129,g_west,x_80,y_-60,l_text:Source%20Sans%20Pro_70_line_spacing_-10_semibold:Anyone%20Can%20Map!%20Inspiration%20and%20an%20introduction%20to%20the%20world%20of%20mapping/blog-social-card-1.1"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image:width"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"1280"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image:height"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"640"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"twitter:card"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"summary_large_image"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"twitter:image"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"https://res.cloudinary.com/fay/image/upload/w_1280,h_640,c_fill,q_auto,f_auto/w_860,c_fit,co_rgb:232129,g_west,x_80,y_-60,l_text:Source%20Sans%20Pro_70_line_spacing_-10_semibold:Anyone%20Can%20Map!%20Inspiration%20and%20an%20introduction%20to%20the%20world%20of%20mapping/blog-social-card-1.1"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"twitter:site"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"@colbyfayock"</span> /&gt;</span>
</code></pre>
<h2 id="heading-looking-for-other-ways-to-optimize-and-analyze-your-content">Looking for other ways to optimize and analyze your content?</h2>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-add-a-social-media-image-to-your-github-project/">How to Add a Social Media Image to Your Github Project Repository</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/making-sense-of-google-analytics-and-the-traffic-to-your-website/">How to Make Sense of Google Analytics and the Traffic to Your Website</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-set-up-and-track-youtube-channel-performance-with-google-analytics/">How to set up and track YouTube Channel performance with Google Analytics</a></li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add a Social Media Image to Your Github Project Repository ]]>
                </title>
                <description>
                    <![CDATA[ Sharing links without a social image can turn powerful content into a flop. How can we take advantage of the real estate social media gives us when sharing our hard work on Github? Want to skip ahead of the “what” and “why”?  Jump to the “how”! https... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-a-social-media-image-to-your-github-project/</link>
                <guid isPermaLink="false">66b8e32e68c5b9f37d1d1aeb</guid>
                
                    <category>
                        <![CDATA[ #content marketing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ marketing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 26 Feb 2020 15:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/02/github-social-images.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sharing links without a social image can turn powerful content into a flop. How can we take advantage of the real estate social media gives us when sharing our hard work on Github?</p>
<p>Want to skip ahead of the “what” and “why”?  <a class="post-section-overview" href="#heading-adding-an-image-to-your-github-repo">Jump to the “how”</a>!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/KRNiqCLlPNQ" 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-impact-of-images-in-social-media">Impact of images in social media</h2>
<p>Any social media feed is a flurry of content that can be difficult to completely digest. As you swipe or scroll through the feeds, you’ll be met with some text-based content and a ton of media.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/02/twitter-feed-media-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Twitter feeds with media on TweetDeck</em></p>
<p>And this is for a good reason! As you scan the example above, what stands out? Not the <a target="_blank" href="https://twitter.com/uiswarup/status/1230781530907324417">Dust Particle retwee</a>t from <a target="_blank" href="https://twitter.com/CodePen">CodePen</a> on the left, but the AWS in the middle, dog nose in the top left, and Al Pacino in the top right among the other large media posts.</p>
<p>Images, gifs, and videos are a good way to provide eye-catching content. As a bonus, they typically take up much more space in the feed, making it even more likely you’ll get a chance to see it.</p>
<h2 id="heading-the-bare-minimum-on-github">The bare minimum on Github</h2>
<p>When sharing your Github projects to social media, the default option is pretty bland. Take this tweet as an example:</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1226204581824204804"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Github gives us a simple <a target="_blank" href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards">social media card</a> that’s comprised of our Github avatar, the path of our project, and the short description from the top of the page.</p>
<p>Now it’s great they even show a card to begin with and to be fair, what would they show? But this tweet is going to get skipped pretty easily in people’s feeds between big, shiny images.</p>
<h2 id="heading-github-social-media-images">Github social media images</h2>
<p>Luckily, Github provides us with a nice and easy way to add an image to each repo that can help the content we share work for itself a little more.</p>
<p>With a little creative work, we can upload an image that will that will take advantage of the space on people’s feeds and get your work the attention it deserves.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/02/canva-social-media-example-2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Example of a social image using Canva</em></p>
<p>Before we dive in, it should be noted that you must have the appropriate access to be able to modify the settings of the repository in order to change the image. If you created the repo, chances are you have this access.</p>
<h2 id="heading-finding-or-creating-an-image">Finding or creating an image</h2>
<p>Before we upload an image, we need an image in the first place.  You can go one of two routes: finding an image or creating a new one.</p>
<p>If you want to go the simple route, you can look around for free to use images that are pretty easy to find on the web. A favorite of mine is <a target="_blank" href="https://unsplash.com/">Unsplash</a>, as you’ll typically find high quality images, but there are a ton of others you can find with <a target="_blank" href="https://www.google.com/search?q=free+stock+photos">a simple search</a>. However, adding photos is usually better served for blog posts and content-based posts.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/freeCodeCamp/status/1230190815605121024"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>The better route is to create your own. The advantage of creating an image is you can customize it with large text to catch some extra attention in the feed. </p>
<p>There are a <a target="_blank" href="https://zapier.com/blog/graphic-design-tools-for-social-media-images/">ton of free tools</a> that are available that allow you to create an image starting with a social media specific template, so you don’t need to spend the money on <a target="_blank" href="https://www.adobe.com/products/photoshopfamily.html">Photoshop</a> to get there.</p>
<p>The only requirement for your image is that its size must be at least 640×320px. If possible, strive for at least 1280×640px to make sure the image is showing at a high resolution in feeds.</p>
<h2 id="heading-adding-an-image-to-your-github-repo">Adding an image to your Github repo</h2>
<p>Once we have our image, we’re just left with adding the image to our repo, which is arguably the simpler part.</p>
<p>First, navigate to the Settings of your Repo using the tab navigation towards the top of the page. As a reminder, you need to be able to modify the settings to change the image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/02/github-settings-page-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Github Settings page</em></p>
<p>Next, scroll down to the Social Preview section, where if you don't currently have an image set, you’ll find a big empty rectangle with an edit button in the bottom left corner.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/02/github-upload-social-image.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Upload social image on Github</em></p>
<p>Click the Edit button, select Upload an image, then find your image on your computer and select the file.</p>
<p>Once selected, your image file will be uploaded to Github and set as your social image!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/02/github-social-media-image.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Social preview on Github</em></p>
<h2 id="heading-previewing-your-new-image">Previewing your new image</h2>
<p>When you’re done uploading your image, you can make your way to your favorite social platform and give it a try. Posting to Twitter for example will now show a nice big image instead of your small Github avatar!</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1230604806324183046"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>As you can see, this tweet is maximizing the space available and getting the point across about what the project is about.</p>
<h2 id="heading-a-few-tips-when-creating-images">A few tips when creating images</h2>
<h3 id="heading-large-simple-text">Large, simple text</h3>
<p>It can be easy to get caught up in over-designing a graphic or not paying enough attention to the font size. Make sure you use big letters with a font that’s easy to read, so when someone scrolls past your post, they can understand it and not just skip it.</p>
<h3 id="heading-add-a-logo">Add a logo</h3>
<p>Do you have a logo for your project? Or is it a plugin for a specific tool? Try adding a logo to immediately give context about what the project is about!</p>
<h3 id="heading-maximize-resolution">Maximize resolution</h3>
<p>No one likes looking at poor quality images. Take advantage of all of those precious pixels and upload an image that will translate to high quality in a feed. Github particularly recommends an image size of at least 1280×640px.</p>
<h2 id="heading-have-any-other-tricks-to-maximize-sharing-on-social">Have any other tricks to maximize sharing on social?</h2>
<p>Share your favorite tips with me on <a target="_blank" href="https://twitter.com/colbyfayock">Twitter</a>!</p>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to block unproductive websites and boost your productivity using JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ By Madhav Bahl Tired of wasting your time on various unproductive websites? Why not make a script which would help you limit the time you spend on these websites? Does this sound familiar…? Just another day, scrolling through my Social Media feed an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-block-unproductive-websites-and-boost-your-productivity-using-javascript-20534011cccc/</link>
                <guid isPermaLink="false">66c34f389972b7c5c7624e88</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 01 Oct 2018 16:56:07 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*9-xBdZXdd_FT1X-DTTX75A.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Madhav Bahl</p>
<p>Tired of wasting your time on various unproductive websites? Why not make a script which would help you limit the time you spend on these websites?</p>
<p>Does this sound familiar…?</p>
<blockquote>
<p>Just another day, scrolling through my <strong>Social Media</strong> feed and watching memes. I found that it had been been 4 hours since I’d been sitting at the same position and “Doing Nothing”. I hated it! I had to do something about it. It came into my mind, why not make some script which would help me limit the amount of time I spend on these websites?</p>
</blockquote>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*bIF5jb6o8DpD_JvS.jpg" alt="Image" width="398" height="449" loading="lazy">
_Stop scrolling through your feed, and do something xD (source: [https://www.writerswrite.com/writingmemes/](https://www.writerswrite.com/writingmemes/" rel="noopener" target="<em>blank" title="))</em></p>
<p>How about making a script which would block all these websites? The script allows you to use them only at some specific given hours of a day. Sounds legit! Let’s do it. :-)</p>
<p>Yes, I know there are many easy methods to block any website. Just download some Chrome plugin, or rather any software which would do this for us. Well yeah, quite easy! But come on, we are developers, we don’t do these things! When we need something, we develop scripts for it rather than using some random trick to do the work…right?!</p>
<p>If you want to download the script directly, you can do so from <a target="_blank" href="https://github.com/MadhavBahlMD/Control-Yourself/blob/master/JavaScript/blocker.js">here</a>.</p>
<h3 id="heading-lets-get-started">Let’s Get Started!</h3>
<p>Unlike my other tutorial articles, you won’t need any directory structure or a dev environment set up for this project. All you need is NodeJS installed on your system and a good text editor. You can make this script using any language of your choice which supports file handling. I chose JavaScript because I love it!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*Fhqu52MS8kbjNWKY.png" alt="Image" width="800" height="600" loading="lazy">
_<strong>I ❤️ JavaScript!</strong> (source: [https://brendaneich.com/2015/06/from-asm-js-to-webassembly/](https://brendaneich.com/2015/06/from-asm-js-to-webassembly/" rel="noopener" target="<em>blank" title="))</em></p>
<h3 id="heading-the-background-idea">The Background Idea</h3>
<p>The idea behind this blocker we are going to make is very simple. There is a file named <code>hosts</code>. We can add the URL of any website and the URL of a website to which we want to redirect the former website to. Something like this:</p>
<pre><code><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>    www.facebook.com
</code></pre><p>Now, whenever we try to open Facebook, it will be redirected to 127.0.0.1 (localhost) automatically. This will indirectly block the website.</p>
<p>The hosts file which I am talking about is present in <code>C:\Windows\System32\drivers\etc\hosts</code> if you use Windows. If you are a Mac or Linux user, the location of that file is: <code>/etc/hosts</code>.</p>
<h3 id="heading-lets-modify-the-file">Let’s Modify The File…</h3>
<p>Before starting the code, let’s try to modify the file and see if it works. Please note that only the user with administrator rights can modify this file. If you are on Windows you can right click on that file and open as administrator. If you are using Linux, you can use the sudo command. I am using nano to open the file, you can use any other editor of your choice.</p>
<pre><code>sudo nano /etc/hosts
</code></pre><p>After you type this command, it will ask you to enter your password. You can enter it and open your file. Let’s try it out :)</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*0MXqMMIcuM34PF780gIO-w.gif" alt="Image" width="800" height="459" loading="lazy"></p>
<p>Alright, so we appended our “to be blocked” website in the hosts file, now let’s check it out whether it worked or not. To check it, go to any web browser, and go to that website.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*l-CTDo6vZEcLlSUu.png" alt="Image" width="800" height="436" loading="lazy">
<em>Yippee! It worked :3</em></p>
<p>Now that we’ve checked that our concept is correct, let’s code the blocker.</p>
<h3 id="heading-1-setting-up-the-variables">1. Setting up the variables</h3>
<p>As I said earlier, there is no need of huge directory structuring or setting up of a dev environment. All you need to do is make a JavaScript file (say, <code>blocker.js</code>) and start coding.</p>
<p>First of all, we need to import <code>fs</code> (file system) Node module through which we will be making changes to our hosts file. You can read the complete documentation of fs <a target="_blank" href="https://nodejs.org/api/fs.html">here</a>.</p>
<pre><code><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
</code></pre><p>Now, we will need to initialize 3 variables:</p>
<ol>
<li><strong>filePath</strong> — To store the path of hosts file</li>
<li><strong>redirectPath</strong> — For the redirection path (here, localhost)</li>
<li><strong>websites</strong> — Array of websites to be blocked</li>
</ol>
<p>Also, we will be making a variable named <code>delay</code>. This variable will store the value of time duration (in milliseconds) after which our script will repeat itself. Basically the idea is to keep the script running all the time to check whether it is the time to block/unblock the websites. To keep it running we will be using <code>setInterval()</code> method in JavaScript. We can also use <code>while (true) {}</code> to make an infinite loop.</p>
<p>Right now we are keeping the time after which the function repeats itself constant (say, 10 seconds). But, this script can be made smarter by setting the value of delay equal to the time difference between current time and time at which the state of script (block/unblock) has to be changed. Doing this is much more easier than what it feels like — so I want you (the reader) to do it yourself, and drop me a <a target="_blank" href="http://www.madhavbahl.tech/">mail</a>, I would love to hear from you ?</p>
<pre><code><span class="hljs-keyword">const</span> filePath = <span class="hljs-string">"/etc/hosts"</span>;<span class="hljs-keyword">const</span> redirectPath = <span class="hljs-string">"127.0.0.1"</span>;<span class="hljs-keyword">let</span> websites = [ <span class="hljs-string">"www.someRandomWebsite.com"</span>,<span class="hljs-string">"anotherWebsite.com"</span> ];<span class="hljs-keyword">let</span> delay = <span class="hljs-number">10000</span>;
</code></pre><p><strong>Note*</strong> If you are a Windows user, store this in the filePath variable: C:\Windows\System32\drivers\etc\hosts</p>
<h3 id="heading-2-the-blocker-function">2. The blocker function</h3>
<p>We will now make a blocker function. We call it from the setInterval method to keep it running after every given time interval.</p>
<pre><code><span class="hljs-keyword">let</span> blocker = <span class="hljs-function">() =&gt;</span> {    ....    ....};
</code></pre><p><strong>Now we will fill the code inside our blocker function.</strong></p>
<h4 id="heading-inside-blocker-getting-current-time">Inside blocker: Getting current time</h4>
<p>First of all we need to get the current time, and then check whether it is the time to block the website or unblock it.</p>
<pre><code><span class="hljs-keyword">let</span> date = <span class="hljs-keyword">new</span> date();<span class="hljs-keyword">let</span> hours = date.getHours();<span class="hljs-keyword">if</span>(hours &gt;= <span class="hljs-number">14</span> &amp;&amp; hours &lt; <span class="hljs-number">18</span>) {    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Time to block websites'</span>);    ....    ....} <span class="hljs-keyword">else</span> {    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Time to unblock websites'</span>);    ....    ....}
</code></pre><h4 id="heading-inside-blocker-inside-if-the-if-condition-is-true">Inside blocker: Inside If — The If condition is true</h4>
<p>Now we need to read the hosts file and convert the fetched data to string (the <code>readFile()</code> function will return the buffer data which needs to be converted into string).</p>
<p>After reading the file, we need to check whether the each website and redirect path is present in the hosts file or not. If it is present, then we can ignore it. Otherwise, we need to append <code>redirectPath websiteURL</code> to it which will look something like this:</p>
<pre><code><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>    www.someRandomWebsite
</code></pre><p>To implement this, we will use a for loop. The loop will iterate through each URL in the websites array and check whether it exists inside the file. To do this, we will use <code>indexOf()</code> method of strings. If the value is greater than zero, i.e. the given website is present inside the hosts file, we can simply ignore. Otherwise, if the value is not greater than zero, we need to append the redirectPath and website URL (separated by space) to the file.</p>
<pre><code>fs.readFile(filePath, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {    fileContents = data.toString();    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>;i&lt;websites.length;i++) {        <span class="hljs-keyword">let</span> addWebsite = <span class="hljs-string">"\n"</span> + redirectPath + <span class="hljs-string">" "</span> + websites[i];        <span class="hljs-keyword">if</span> (fileContents.indexOf(addWebsite) &lt; <span class="hljs-number">0</span>) {            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Website not present in hosts file'</span>);            fs.appendFile(filePath, addWebsite, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {                <span class="hljs-keyword">if</span> (err)  <span class="hljs-keyword">return</span> <span class="hljs-built_in">console</span>.log(err);                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'File Updated Successfully'</span>);            });        } <span class="hljs-keyword">else</span> {            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Website is present'</span>);        }    }});
</code></pre><h4 id="heading-inside-blocker-inside-else-if-condition-is-false">Inside blocker: Inside Else — If condition is false</h4>
<p>If the condition is false, we need to check whether the websites in the list are there in the hosts file. If they are present we need to delete them.</p>
<p>For deleting, we will use a simple trick. We will read the file line by line. We create an empty string and check whether the current line contains any of the websites present in the list. If yes, we simply ignore it. Otherwise we will add that line to the string we initialized. After checking the last line, we will simply replace the current content of the file by this completeContent string.</p>
<p>The code to do so is very easy. First initialize an empty string (<code>completeContent</code>). Then read the file line by line. Follow the steps given in the code below. Then replace the file’s content by completeContent variable.</p>
<pre><code><span class="hljs-comment">// Initialize the empty stringlet completeContent = '';</span>
</code></pre><pre><code><span class="hljs-comment">// Read the file line by linefs.readFileSync(filePath)    .toString()    .split()    .forEach((line) =&gt; {        ....        ....        ....        // Do the below given procedure to update completeContent });</span>
</code></pre><pre><code><span class="hljs-comment">// Replace the file contents by `completeContent` variablefs.writeFile(filePath, completeContent, (err) =&gt; {    if (err) {        return console.log('Error!', err);    }});</span>
</code></pre><p>Now that we have access to each line, we can check whether this line contains any website by using a flag and a for loop. We set the flag to 1 (or true) and then run a loop to iterate through the list of websites. If the line contains the current website (we will check it using <code>string.indexOf(substring)</code> method), reset the flag to 0 and break the current loop. Outside the loop we check, if the flag is 1 (or true) we append the current line into the <code>completeContent</code> variable.</p>
<p><strong>Please also note</strong> that if the flag is 1, we also check that whether the current line is last line or not. If it is not the last line, we append the current line into the <code>completeContent</code> string along with a <code>"\n"</code> so that the next line will be appended into <code>completeContent</code> from a new line (or with a line break). Follow along the following code inside the forEach() of above code block.</p>
<pre><code><span class="hljs-keyword">let</span> flag = <span class="hljs-number">1</span>;<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i&lt;websites.length; i++) {    <span class="hljs-keyword">if</span> (line.indexOf(websites[i]) &gt;= <span class="hljs-number">0</span>) { <span class="hljs-comment">// line contains website        flag = 0;        break;    }}</span>
</code></pre><pre><code><span class="hljs-keyword">if</span>(flag == <span class="hljs-number">1</span>) {    <span class="hljs-keyword">if</span> (line === <span class="hljs-string">''</span>)           completeContent += line;    <span class="hljs-keyword">else</span>         completeContent += line + <span class="hljs-string">"\n"</span>;}
</code></pre><h3 id="heading-3-running-the-code-for-the-blocker-function">3. Running the code for the blocker function</h3>
<p>Here is the code for the blocker function just in case you were confused with the distributed code in section 2:</p>
<p>Now, for running this function continuously, we can go for <code>while (true) {}</code> as an infinite loop. Inside it we can give some time delay so that it doesn't occupy the processor continuously.</p>
<p>Or, a better option is to use the <code>setInterval()</code> function. This keeps repeating the blocker function after a specific interval of time. But, <code>setInterval()</code> will run for the first time after the specified delay. Therefore we will have to call the blocker function once before the setInterval function.</p>
<pre><code>blocker();<span class="hljs-built_in">setInterval</span>(blocker, delay);
</code></pre><h3 id="heading-4-all-done-lets-check-our-script">4. All Done! Let’s check our script</h3>
<p>Time to run our script. To run the script, open the present working directory in a terminal and type in the following command:</p>
<pre><code>sudo node blocker.js
</code></pre><p>If you are a Windows user, you can open the command prompt as administrator, go to the project directory, and then run the command:</p>
<pre><code>node blocker.js
</code></pre><p>Please note that just for checking purposes, I am blocking <code>facebook.com</code>. Here is the test run:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*3csxXZL_6AjyODUwXucfbw.gif" alt="Image" width="1305" height="701" loading="lazy">
<em>Yuss! We Made It ❤</em></p>
<h3 id="heading-5-the-final-step">5. The Final Step...</h3>
<h4 id="heading-for-mac-and-linux">For Mac and Linux</h4>
<p>You can schedule this script to run whenever someone starts the system using crontab. Cron is a time-based job scheduler in Unix-like computer operating systems. You can read more about cron <a target="_blank" href="https://opensource.com/article/17/11/how-use-cron-linux">here</a>.</p>
<p>So we will be adding our command through which we run the script (<code>sudo node blocker.js</code>) in the cron table. Doing this is very simple: open the terminal using <code>ctrl+alt+t</code>, then open crontab using <code>sudo crontab -e</code>. This command will open the cron table.</p>
<p><strong>Note</strong> that we used <code>sudo crontab</code>, not <code>crontab</code>. This is will enable us to modify the cron table.</p>
<p>Once you have it open, add this line at the end (replace <code>path-to-script</code> with the path of your project directory):</p>
<pre><code>@reboot node /path-to-script/blocker.js
</code></pre><p>That’s it! Doing this will run your script every time system reboots.</p>
<h4 id="heading-for-windows">For Windows</h4>
<p>The script can be scheduled to run every time the system starts in Windows also. <a target="_blank" href="https://www.howtogeek.com/138159/how-to-enable-programs-and-custom-scripts-to-run-at-boot/">Here</a> is a very good article which tells how to do so.</p>
<h3 id="heading-where-to-go-from-here">Where to go from here?</h3>
<p>Are you an open source enthusiast? Want to contribute to this project?<br>I am starting a new Open Source project named <strong>“Control-Yourself”</strong> which will be a desktop application made using <a target="_blank" href="https://electronjs.org/">Electron</a>. The features will include:</p>
<ul>
<li>taking inputs from users about which times they want to block which websites</li>
<li>tracking the time a user spends watching social media websites</li>
<li>a Pomodoro timer</li>
<li>and a todo list application with daily progress report of productivity.</li>
</ul>
<p>Check out the <a target="_blank" href="https://github.com/MadhavBahlMD/Control-Yourself">repository</a>, and add a comment “interested” on the issue you are interested to work on.</p>
<p>Now, let me give you the complete code with proper comments which will help you understand the code:</p>
<p><strong>Complete Code (blocker.js)</strong></p>
<h3 id="heading-thats-it">That’s it</h3>
<p>Have you found the article helpful?</p>
<p><a target="_blank" href="http://madhavbahl.tech/subscribe/">Subscribe to TheLeanProgrammer</a> to be the first one to get notified from me for future updates.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*L-3kS5mz7jp4e8zV8WLoLQ.png" alt="Image" width="320" height="301" loading="lazy"></p>
<p>Feel free to reach out to me anytime if you want to discuss something :D</p>
<p>I would be more than happy if you send your feedback or suggestions, or if you ask questions. Moreover, I love to make new friends — so just drop me a mail.</p>
<blockquote>
<p>Thanks a lot for reading till end. You can contact me in case if you need any assistance:<br>Email: madhavbahl10[at]gmail[dot]com<br>Web: <a target="_blank" href="http://madhavbahl.ml/">http://madhavbahl.tech/</a><br>Github: <a target="_blank" href="https://github.com/MadhavBahlMD">https://github.com/MadhavBahlMD</a><br>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/madhavbahl/">https://www.linkedin.com/in/madhavba<em>hl/</em></a></p>
</blockquote>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to predict likes and shares based on your article’s title using Machine Learning ]]>
                </title>
                <description>
                    <![CDATA[ By Flavio H. Freitas Choosing a good title for an article is an important step in the writing process. The more interesting the title seems, the higher the chance a reader will interact with the whole thing. Furthermore, showing the user content they... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-predict-likes-and-shares-based-on-your-articles-title-using-machine-learning-47f98f0612ea/</link>
                <guid isPermaLink="false">66c353f2d73001a6c0054c03</guid>
                
                    <category>
                        <![CDATA[ data scientist ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 21 Sep 2018 22:24:18 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*gQRQ6x29YFA_ngSpaoDCUw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Flavio H. Freitas</p>
<p>Choosing a good title for an article is an important step in the writing process. The more interesting the title seems, the higher the chance a reader will interact with the whole thing. Furthermore, showing the user content they prefer (to interact with) increases the user’s satisfaction.</p>
<p>This is how my final project from the <a target="_blank" href="https://udacity.com/course/machine-learning-engineer-nanodegree--nd009">Machine Learning Engineer Nanodegree</a> specialization started. I just finished it, and I feel <em>so proud and happy</em> ? that I wanted to share with you some insights I’ve had about the whole flow. Also, I promised Q<a target="_blank" href="https://medium.com/@quincylarson">uincy Larson</a> this article when I finished the project.</p>
<p>If you want to see the final technical document <a target="_blank" href="https://github.com/flaviohenriquecbc/machine-learning-capstone-project/blob/master/final-report.pdf">click here</a>. If you want the implementation of the code, check it out <a target="_blank" href="https://github.com/flaviohenriquecbc/machine-learning-capstone-project/blob/master/title-success-prediction.ipynb">here</a> or fork my project on <a target="_blank" href="https://github.com/flaviohenriquecbc/machine-learning-capstone-project">GitHub</a>. If you just want an overview using layperson’s terms, this is the right place — continue reading this article.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*gQRQ6x29YFA_ngSpaoDCUw.png" alt="Image" width="800" height="290" loading="lazy">
<em>FreeCodeCamp Medium post on Twitter</em></p>
<p>Some of the most used platforms to spread ideas nowadays are Twitter and Medium (you are here!). On Twitter, articles are normally posted including external URLs and the title, where users can access the article and demonstrate their satisfaction with a like or a retweet of the original post.</p>
<p>Medium shows the full text with tags (to classify the article) and claps (similar to Twitter’s likes) to show how much the users appreciate the content. <em>A correlation between these two platforms can bring us valuable information.</em></p>
<h3 id="heading-the-project">The project</h3>
<p>The problem that I defined was a classification task using supervised learning: <em>Predict the number of likes and retweets an article receives based on the title.</em></p>
<p>Correlating the number of likes and retweets from Twitter with a Medium article is an attempt to isolate the effect of the number of reached readers and the number of Medium claps. Because the more the article is shared on different platforms, the more readers it will reach and the more Medium claps it will (likely) receive.</p>
<p>Using only the Twitter statistic, we’d expect that the articles reached initially almost the same number of readers (those readers being the followers of the freeCodeCamp account on Twitter). Their performance and interactions, therefore, would be limited to the characteristics of the tweet — for example, the title of the article. And that is exactly what we want to measure.</p>
<p>I chose the <a target="_blank" href="https://twitter.com/freecodecamp">freeCodeCamp account</a> for this project because the idea was to limit the scope of the subject of the articles and better predict the response on a specific field. The same title can perform well in one category (e.g. Technology), but not necessarily in a different one (e.g. Culinary). Also, this account posts the title of the original article and the URL on Medium as the tweet content.</p>
<h3 id="heading-how-does-the-data-look">How does the data look?</h3>
<p>The first step of this project was to get the information from Twitter and Medium and then correlate it. The dataset can be found <a target="_blank" href="https://github.com/flaviohenriquecbc/machine-learning-capstone-project/blob/master/dataset/dataset-tweets-final.json">here</a> and it has 711 data points. This is how the dataset looks like:</p>
<h3 id="heading-analyzing-and-learning-with-the-data">Analyzing and learning with the data</h3>
<p>After analyzing the dataset and plotting some graphics, I found interesting information about it. For these analyses, <strong>the outliers were removed,</strong> and I just considered the <strong>25% top performers</strong> for each feature (retweet, like, and clap).</p>
<p>So let’s take a look at what the numbers say for freeCodeCamp articles written on Medium and shared on Twitter.</p>
<h4 id="heading-what-is-a-good-title-length">What is a good title length?</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Mm7zCNram85z-qmQ2PYGgA.png" alt="Image" width="800" height="391" loading="lazy">
<em>Title length performance</em></p>
<p>Writing titles that have a length <strong>greater than 50 and less than 110</strong> characters helps to increase the chances of a successful article.</p>
<h4 id="heading-what-is-a-good-number-of-words-in-the-title">What is a good number of words in the title?</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fQ1kXH82jeikkfUtsl7baA.png" alt="Image" width="800" height="389" loading="lazy">
<em>Number of words performance</em></p>
<p>The most effective number of words in the title is <strong>9 to 17</strong>. To optimize the number of retweets and likes, try something from 9 to 18 words, and for claps from 7 to 17.</p>
<h4 id="heading-which-are-the-best-categories-to-tag">Which are the best categories to tag?</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*NNmbj8LjKK4Mj1eBvRD2wQ.png" alt="Image" width="800" height="358" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*spIxtLO9qD042AP-XFiicA.png" alt="Image" width="800" height="351" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*WSluJ1QtQNwukYnW60TU1A.png" alt="Image" width="800" height="364" loading="lazy"></p>
<p><strong>Programming</strong>, <strong>Tech</strong>, <strong>Technology</strong>, <strong>JavaScript</strong> and <strong>Web Development</strong> are categories you should consider when tagging your next article. They appear for all the three features as a good indicator.</p>
<h4 id="heading-which-are-the-best-words-to-use">Which are the best words to use?</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*f1vJmkiXf0Nlxc9nCU0Vrw.png" alt="Image" width="800" height="391" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*vKj2TVnOSgLHWuv3WiAZUA.png" alt="Image" width="800" height="392" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Y4PqnyR2dF4da5WWKuqS1g.png" alt="Image" width="800" height="376" loading="lazy"></p>
<p>In this lexical analysis, you’ll notice that some words get much more attention on the freeCodeCamp community than others. If the intention is to make the articles reach further in numbers, talking about JavaScript, React or CSS will increase how much it’s appreciated. Using the words “learn” or “guide” to describe will also make the probability higher.</p>
<h3 id="heading-using-machine-learning">Using Machine Learning</h3>
<p>OK! After taking a look at the data and extracting some information from it, the goal was to create a Machine Learning model that makes predictions of the number of retweets, likes, and claps based on the title of the article.</p>
<p>Predicting the number of retweets, likes, and claps of an article can be treated as a classification problem, and that is a common task of machine learning (ML). But for this, we need to use the output as discrete values (a range of numbers). The input will be the title of the articles with each word as a token (t1, t2, t3, … tn), the title length, and the number of words in the title.</p>
<p>The ranges for our features are:</p>
<ul>
<li>Retweets: 0–10, 10–30, 30+</li>
<li>Likes: 0–25, 25–60, 60+</li>
<li>Claps: 0–50, 50–400, 400+</li>
</ul>
<p>And finally, after preprocessing our dataset and evaluating some models (everything fully described <a target="_blank" href="https://github.com/flaviohenriquecbc/machine-learning-capstone-project/blob/master/final-report.pdf">here</a>), we reached the conclusion that the MultinomialNB model performed better for retweets reaching an accuracy of 60.6%. Logistic regression reached 55.3% for likes and 49% for claps.</p>
<p>As an experiment for this article, I ran the prediction of the title of this article and the model predicted that:</p>
<p>It will have 10–30 retweets and 25–60 favorites on Twitter and 400+ claps on Medium.</p>
<p>How is this prediction? ?</p>
<p><a target="_blank" href="https://medium.com/@flaviohfreitas"><em>Follow me</em></a> <em>if you want to read more of my articles</em> ? <em>And if you enjoyed this article, be sure to like it give me a lot of claps — it means the world to the writer.</em></p>
<p><strong>Flávio H. de Freitas</strong> is an Entrepreneur, Engineer, Tech lover, Dreamer and Traveler. Has worked as <strong>CTO</strong> in <strong>Brazil</strong>, <strong>Silicon Valley and Europe</strong>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How my social network for sharing photos of knees will save the world ]]>
                </title>
                <description>
                    <![CDATA[ By Roger Collier There are two things every person has in common: knees. Your friends have knees. Your coworkers have knees. So does your Aunt Sally. And your Uncle Aloysius. You. Me. Left knee. Right knee. Yet, despite this common bond, people conti... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-my-social-network-for-sharing-photos-of-knees-will-save-the-world-2074367d10ad/</link>
                <guid isPermaLink="false">66d460c8d7a4e35e384349ad</guid>
                
                    <category>
                        <![CDATA[ humor ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Life lessons ]]>
                    </category>
                
                    <category>
                        <![CDATA[ satire ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 21 Sep 2018 16:19:27 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*YE_Y-aNfMRQc7FYFGPHBnw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Roger Collier</p>
<p>There are two things every person has in common: knees. Your friends have knees. Your coworkers have knees. So does your Aunt Sally. And your Uncle Aloysius.</p>
<p>You. Me. Left knee. Right knee.</p>
<p>Yet, despite this common bond, people continue to argue, fight and hurt one another. There is conflict everywhere, both online and in the real world. But what if it were possible, after centuries of squabbling, to bring peace and harmony to all mankind through our shared identity. Well, that day has finally come.</p>
<p>Introducing Deez Knees, the first and only social network for sharing photos of knees — only knees.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Eip0YkIYcNF4zOYyZn6RjSU3gJR9ac0kpJtT" alt="Image" width="800" height="518" loading="lazy"></p>
<h3 id="heading-why-knees">Why knees?</h3>
<p>I created Deez Knees to unify humanity. Some of us are taller than others. Some of us have dark hair and others have light hair. But when it comes to knees, there is little variety. A knee has only two parts: an interior component (the knee bone) and an exterior component (the knee skin). This is true for my knees, your knees, his knees and her knees. By sharing pictures of knees, we remind ourselves that deep down, beneath the knee skin, we are all basically the same.</p>
<blockquote>
<p>Deez Knees promotes unity</p>
</blockquote>
<p>You know what else Deez Knees promotes? Diversity. “Hey, hold on a second mister,” you might be thinking. “You just said all knees are the same, so how can your amazing new creation promote both unity and diversity?”</p>
<p>That, my friend, is the power of Deez Knees.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/QXWTJQhJjNliBbTTlAyU7V1FqC5w3Re0G6s8" alt="Image" width="800" height="424" loading="lazy"></p>
<h3 id="heading-scalable-configurable-technologicable">Scalable, configurable, technologicable</h3>
<p>The core technology behind Deez Knees is a sophisticated knee-recognition algorithm. You can try to post a photo of something else, but it won’t work. Upload a picture of your elbow? No dice. Share a pic of your butt? Try Instagram.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/A8sXhB4t2aK4y3JohYcJLpMcWg-S5tNyaB51" alt="Image" width="800" height="282" loading="lazy"></p>
<p>Deez Knees is fully integrateable across all digital platforms — vertical, horizontal and diagonal. The user interface is intuitive, responsive and gratuitive. Your personal information will remain secure. We use triple-bypass, hexagonalical encryption with cross-key hashtags. All data on Deez Knees are stored not only in the cloud, but also in the mist and the yawn.</p>
<p>Do we use blockchain? Yes, indeed — 27 blockchains, to be precise. Users can also select the cryptocurrency of their choice: Bitcoin, CyberDime or WebDough. Our network is lightening fast. Images load quickly thanks to our middle-out compression algorithm, which is based on science and math. Deez Knees is also chocked full of nanotechnology and artificial intelligence.</p>
<blockquote>
<p>Digital, Advanced Tech, The Future — Deez Knees</p>
</blockquote>
<p><img src="https://cdn-media-1.freecodecamp.org/images/gLzORlhxZB6TQse97zA1VBJ5pzGdjqysPSbf" alt="Image" width="800" height="447" loading="lazy"></p>
<h3 id="heading-making-the-world-a-better-place">Making the world a better place</h3>
<p>Most tech entrepreneurs care only about money and fame. They want to be the next high-profile billionaire, grace the covers of magazines, hobnob with celebrities. Not me. I created Deez Knees for you. For you and your knees.</p>
<p>Will there be advertising on Deez Knees? That goes without saying. Will I give your personal information to marketing companies? Yes, of course, but not for free. Will I sell Deez Knees to Facebook the first chance I get? You betcha.</p>
<p>But don’t let these minor business details distract you. Deez Knees was created for a higher purpose. There is too much bad in the world. Let’s inject a little bit of good. Actually, make that a whole lot of good.</p>
<p>I don’t know about you, but I think this world of ours is worth saving. Enough with the crime and the wars. Enough with the nasty YouTube comments and the silly Twitter beefs. Why so hostile? Friend, you have only one life. One life and two knees. Use them well.</p>
<p>That’s where Deez Knees comes in. Instead of clapping back at that internet troll, share a photo of your knees. You’ll both soon realize you aren’t so different after all. Instead of bashing Republicans, or Democrats or Saskatchewanians, scroll through your Deez Knees feed and reflect on how we are all diverse yet the same, different yet not so different, unequal in some ways yet — really, when you think about it, really think about it— always equal in all the ways that count.</p>
<blockquote>
<p>Deez Knees turns nasty into nicety</p>
</blockquote>
<p>It’s time, my friend. Time to make this world a better place. Time to bring light into darkness. Time to turn conflict into proflict. All we need is you, me and Deez Knees. We can do it. Two knees at a time.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to write a great technical blog post ]]>
                </title>
                <description>
                    <![CDATA[ By Sashko Stubailo Five steps to get from idea to polished result I’ve been working in the open source community for almost 5 years now, building and promoting developer tools including Meteor and Apollo. In that time, I’ve found that blogging is one... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/</link>
                <guid isPermaLink="false">66d4614b246e57ac83a2c7d3</guid>
                
                    <category>
                        <![CDATA[ Blogging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ writing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 10 Aug 2018 21:08:59 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*QVTcnfXyMXivNu62IJ7JSg.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sashko Stubailo</p>
<h4 id="heading-five-steps-to-get-from-idea-to-polished-result">Five steps to get from idea to polished result</h4>
<p>I’ve been working in the open source community for almost 5 years now, building and promoting developer tools including <a target="_blank" href="https://github.com/meteor/">Meteor</a> and <a target="_blank" href="https://github.com/apollographql/">Apollo</a>. In that time, I’ve found that blogging is one of the most effective ways to spread ideas.</p>
<p>A blog post doesn’t take as long to prepare as a video or conference talk, but is easy to consume and can reach a ton of people. I’ve also personally gotten a ton of benefit out of writing: it has helped me organize my thoughts, teach people about technologies I love, and get my name out there.</p>
<p>Since publishing <a target="_blank" href="https://blog.meteor.com/collaborative-3d-scene-builder-in-50-lines-of-code-3c8ac717c658">my first blog post ever</a> in 2014, I’ve ended up writing <a target="_blank" href="https://medium.com/@stubailo/latest">68 posts</a> so far here on Medium, some with over 50k views and 1000 fans, and edited many posts for my friends and coworkers. Over that time, I’ve picked up a few strategies for taking a post from concept to publication.</p>
<p>In this article, we’ll go over five main steps of my process for writing a post:</p>
<ol>
<li>Find a good topic and commit to it</li>
<li>Make your goals and audience specific</li>
<li>Have a beginning, middle, and end</li>
<li>Get feedback and iterate</li>
<li>Add finishing touches: packaging, publication, and promotion</li>
</ol>
<p>Let’s get right into the first step!</p>
<h3 id="heading-1-find-a-good-topic-and-commit-to-it">1. Find a good topic and commit to it</h3>
<p>You can’t get started on a post unless you have something to write about! When I talk to people who want to start blogging, this is often their main blocker.</p>
<p>The simplest strategy is to write about what you know. If you spent many hours learning about something, and you think you could explain it in just a few minutes, you’re going to provide a lot of value to your readers.</p>
<p>Another idea is to write about an area you think is lacking content. For example, right now there aren’t a lot of posts about how to apply to technical conferences, so content about that could fill a gap in the community.</p>
<p>Here are some specific types of posts you could go for. Examples are from the GraphQL-related posts on the Apollo blog:</p>
<ol>
<li>A step-by-step guide to achieving a specific goal: <a target="_blank" href="https://blog.apollographql.com/loading-data-into-react-natives-flatlist-9646fa9a199b">“Building a great scrollable list in React Native with FlatList”</a> or <a target="_blank" href="https://blog.apollographql.com/simplify-your-react-components-with-apollo-and-recompose-8b9e302dea51">“Simplify your React components with Apollo and Recompose”</a>. These are great for readers that are looking to get in, do a thing, and get out fast.</li>
<li>An in-depth survey on a particular topic: <a target="_blank" href="https://blog.apollographql.com/using-nullability-in-graphql-2254f84c4ed7">“Using nullability in GraphQL”</a> or <a target="_blank" href="https://blog.apollographql.com/the-anatomy-of-a-graphql-query-6dffa9e9e747">“Anatomy of a GraphQL query”</a>. These are useful if you’re targeting a more interested audience that wants to grab a cup of coffee and learn a ton.</li>
<li>A numbered list of useful facts around a common topic: <a target="_blank" href="https://blog.apollographql.com/4-simple-ways-to-call-a-graphql-api-a6807bcdb355">“4 simple ways to call a GraphQL API”</a> or <a target="_blank" href="https://blog.apollographql.com/5-benefits-of-static-graphql-queries-b7fa90b0b69a">“5 benefits of static GraphQL queries”</a>. These are a fun, lightweight read since you don’t have to commit to reading the whole thing, and it’s easy to consume the bite-sized pieces.</li>
</ol>
<p>Now, let me dispel a few common concerns:</p>
<ol>
<li><strong>There is already content out there about this topic.</strong> Don’t let that stop you. Even if your idea has been written about before, you can put your own perspective on it, or make it specific to your situation.</li>
<li><strong>My idea isn’t interesting enough.</strong> A lot of my friends and coworkers don’t end up writing because they are worried their conclusions might be boring or obvious. That’s a normal feeling! If you’re an expert on something, then of course the conclusions you’re writing about will be boring… <em>to you</em>. The key is that people in your audience don’t know that stuff yet.</li>
</ol>
<p>Having said all that, at the end of the day, it’s hard to predict what topics will make a great post and which ones won’t, and often it’s the <em>execution</em> that makes or breaks a post, not a brilliant topic. My main suggestion would be to try writing about several different things and see what works.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/49vpFzkVXA9fHTQoMGGDKEnqVDBIY4-eZ8d7" alt="Image" width="800" height="533" loading="lazy">
_The lightbulb - a universal symbol for ideas. [James Pond on Unsplash](https://unsplash.com/photos/1qkyck-UL3g" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-2-make-your-goals-and-audience-specific">2. Make your goals and audience specific</h3>
<p>Now that you know your topic, you need an audience and goal for your post. Who will be reading it, and what are they going to get out of it?</p>
<p>Your goal needs to be <em>specific</em> so that you can focus all of your energy on one main idea. For this post, the goal couldn’t have just been “write about blogging”. I needed a more specific goal in mind:</p>
<ul>
<li><strong>Audience:</strong> People who want to start blogging, especially about technical topics, but haven’t done it yet.</li>
<li><strong>Goal:</strong> Give people a concrete set of steps and pointers so they can get started.</li>
</ul>
<p>Once you have these, keep your post focused by removing anything that doesn’t contribute, and avoid adding extra detail just because it’s related. I’ve found that relatively concise posts with a 5 - 10 minute read time are the most successful.</p>
<p>Knowing the background of your audience will allow you to tailor your writing to their existing knowledge, and can help determine how you should publish and promote your content. For example, I hope to publish this on freeCodeCamp, because a lot of people in my target audience might already read that publication.</p>
<h3 id="heading-3-have-a-beginning-middle-and-end">3. Have a beginning, middle, and end</h3>
<p>It’s disorienting when a post veers off in a direction you weren’t expecting. Plot twists can be a big benefit in fictional short stories, but it’s easier to consume a technical article if you get exactly what you expect. You can keep your readers on track by giving your post a comfortable structure.</p>
<h4 id="heading-introduction">Introduction</h4>
<p>The first paragraph or two of your post will either convince the reader to stay on or lose their attention. Start off with a little bit of context to help people understand where your post fits into the big picture. Then, tell your audience what they will get out of reading your article. It might be tempting to leave the big reveal for the end, but watch out: if you don’t have a good hook, your readers won’t stick around to find out.</p>
<h4 id="heading-middle">Middle</h4>
<p>Now that you’ve told your readers what to expect, give it to them! Feel free to go into as much detail as you need, and leave sign-posts along the way to orient people. Use a lot of headings, numbered lists, and formatting to help people understand where they are, and enable them to skip around to the content they are most interested in.</p>
<h4 id="heading-conclusion">Conclusion</h4>
<p>Don’t just taper off into the void at the end of the article. If your reader has made it all the way there, they really care. Give them a quick summary of what they learned, a pat on the back for reading, and maybe even something to do next if they’re inspired - a call to action.</p>
<p>The format I’m suggesting here isn’t the most creative, and there are certainly other ways of doing it. But a simple structure is the most direct way to communicate with your readers.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/op8670wN9BlN6an-m9qZpZcFbitHwZnDFief" alt="Image" width="800" height="533" loading="lazy">
_Like road signage, your post’s structure guides people along. [Bart Anestin on Unsplash](https://unsplash.com/photos/bXMbMw560C8" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-4-get-feedback-and-iterate">4. Get feedback and iterate</h3>
<p>You won’t know what people are going to get from your writing until they read it. This is where your assumptions about your topic, goals, the details of the post, and the structure are really put to the test. If you want to have a good result, you can’t skip this step.</p>
<p>It might feel like you’re imposing when you ask for feedback, or you might be worried that it will be negative, but people are more willing to help than you expect. It’s much better to find out how your post could be better before you publish it to the world. When I was putting together this post, I got some super valuable feedback that made the content much better and more focused.</p>
<p>What should you ask your reviewers? My main advice is to leave it as open-ended as possible. Try not to explain up front what you meant to achieve. Hand over the draft as-is, and ask your reviewer what they got out of it or what should be changed. When someone on the Internet comes across your article, they won’t have any extra context and it has to stand on its own.</p>
<p>The main thing to validate from the feedback is: will this post achieve the goals you decided on in step 2? Iterate until you’re confident it will.</p>
<h3 id="heading-5-add-finishing-touches-packaging-publication-and-promotion">5. Add finishing touches: packaging, publication, and promotion</h3>
<p>Now that you have the idea, the goal, the structure, and some feedback, it’s time to polish everything up and ship it.</p>
<h4 id="heading-packaging">Packaging</h4>
<p>Come up with a great title and subtitle, and make sure your post has at least one image. This is what people will see when the post is shared on Twitter or Facebook, and it’s your chance to get people interested in reading.</p>
<p>It’s also important that your post looks and feels professional, so that your content can really shine. You should aim to have no spelling errors, grammar mistakes, or weird formatting in your post. If you have a friend that is great at spotting small details, ask them to read it over before publishing.</p>
<p>The <a target="_blank" href="https://medium.freecodecamp.org/how-to-get-published-in-the-freecodecamp-medium-publication-9b342a22400e">freeCodeCamp article about getting published</a> also has some great tips about writing style and formatting. Since you’ve already put so much work into your post, it’s worth it to add that little extra effort to really polish it up and give it a wider reach.</p>
<p>Finally, make sure to credit anyone whose work you referenced, or who helped review and edit your post.</p>
<h4 id="heading-publication">Publication</h4>
<p>You’re almost there! Pick where you actually publish the post so that it has the best chance of reaching your audience. Medium is generally a great place for technical content, and makes it easy for people to discover your writing.</p>
<p>For bonus points, try to get your post into a relevant publication that will help share your content - in this case, I’ve selected freeCodeCamp because I think this advice will be relevant to their readers. If you’d like to do the same, <a target="_blank" href="https://medium.freecodecamp.org/how-to-get-published-in-the-freecodecamp-medium-publication-9b342a22400e">here are their directions for submitting your post</a>. Publications in your areas of interest are likely also looking for posts, so don’t be afraid to get in touch!</p>
<h4 id="heading-promotion">Promotion</h4>
<p>Once you’ve actually published the post, you’re not done! If you want people to see what you’ve written and get value out of it, make sure to share it in the places where your audience is likely to hang out. This might include Facebook groups, Reddit, Hacker News, LinkedIn, or any other platform. Also, make sure to share your creation on your own social media accounts, like Twitter. Your friends will be excited to read, share, and upvote what you’ve written!</p>
<p>And now, you’re done. Go get a coffee or take a walk - taking a blog post from start to finish is no small feat. Read any feedback and replies from the the community so that you can keep improving. And when you have another idea, go do it again!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/lMYImUN-AHrgvCD74GhFaHyXGkIa6L0zP15z" alt="Image" width="800" height="533" loading="lazy">
_Beautiful presentation can enhance an already delicious donut. [Zach Miles on Unsplash](https://unsplash.com/photos/BE9AifuJfD4" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-theres-no-replacement-for-practice">There’s no replacement for practice</h3>
<p>We just walked through five of the most important things to do when writing a blog post, from coming up with the idea to publishing. Now that you’ve read it, you try applying this advice and see what works for you.</p>
<p>I’ll leave you with one last bit of advice. The main thing I learned from blogging over the last 3 years is that I absolutely can’t predict what content will take off and what will end up being a total dud. Sometimes, I’ll put several days into a post, polishing every corner, and it won’t go anywhere. On the other hand, <a target="_blank" href="https://blog.apollographql.com/graphql-vs-rest-5d425123e34b">“GraphQL vs. REST”</a>, my most-read post ever, was written in a few hours late at night.</p>
<p>So even if your first, or second, or third posts don’t succeed, keep trying new stuff, putting your ideas out there, and improving over time. The world wants to hear what you’ve got to say. Go tell them!</p>
<p><em>Huge thanks to <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined">Anvisha Pai</a>, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined">Angela Zhang</a>, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined">Katie Siegel</a>, and the freeCodeCamp editors for helping review this post.</em></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
