<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[CodeByCorey | Blog by Corey O'Donnell]]></title><description><![CDATA[I am a Full-Stack Web Developer. I hope to provide you with the inspiration and knowledge to start your web development journey.]]></description><link>https://blog.codebycorey.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 06:38:53 GMT</lastBuildDate><atom:link href="https://blog.codebycorey.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Focus an Input Field with a Hotkey in React]]></title><description><![CDATA[Have you ever wondered how to set a hotkey to focus an input field in your React application? Follow along to learn how. We'll even handle showing different hotkeys based on whether the user is on a Mac or Windows system.
Implementation
const SearchI...]]></description><link>https://blog.codebycorey.com/how-to-focus-an-input-field-with-a-hotkey-in-react</link><guid isPermaLink="true">https://blog.codebycorey.com/how-to-focus-an-input-field-with-a-hotkey-in-react</guid><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Wed, 01 May 2024 17:15:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714583533261/03341a7e-73e2-4d7d-b73c-6ef36c0e3d0e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever wondered how to set a hotkey to focus an input field in your React application? Follow along to learn how. We'll even handle showing different hotkeys based on whether the user is on a Mac or Windows system.</p>
<h2 id="heading-implementation">Implementation</h2>
<pre><code class="lang-tsx">const SearchInput: FC = () =&gt; {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null);
  const [actionKey, setActionKey] = useState&lt;string&gt;('');

  useEffect(() =&gt; {
    // check if navigator is defined to prevent unexpected errors if browser does not support
    if (typeof navigator !== 'undefined') {
      if (/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
        setActionKey('⌘K');
      } else {
        setActionKey('CtrlK');
      }
    }

    const handleKeyPress = (event: KeyboardEvent) =&gt; {
      let hotkey = false;
      if (typeof navigator !== 'undefined') {
        if (/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
          hotkey = event.metaKey &amp;&amp; event.key === 'k';
        } else {
          hotkey = event.ctrlKey &amp;&amp; event.key === 'k';
        }
      }

      if (hotkey &amp;&amp; inputRef.current) {
        inputRef.current.focus();
      }
    };

    // Add event listener when the component mounts
    document.addEventListener('keydown', handleKeyPress);

    // Clean up the event listener when the component unmounts
    return () =&gt; {
      document.removeEventListener('keydown', handleKeyPress);
    };
  }, []);

  return (
    &lt;div className="relative"&gt;
      &lt;input
        ref={inputRef}
        type="text"
        className="peer block w-full border-0 px-0 py-3 text-xl focus:ring-0  sm:leading-6"
      /&gt;
      {actionKey &amp;&amp; (
        &lt;div className="absolute inset-y-0 right-0 flex py-3 pr-1.5"&gt;
          &lt;kbd className="inline-flex items-center rounded border border-zinc-300 px-1 font-sans text-xs text-zinc-400"&gt;
            {actionKey}
          &lt;/kbd&gt;
        &lt;/div&gt;
      )}
    &lt;/div&gt;
  );
};
</code></pre>
<ul>
<li><p>We use the <code>useRef</code> hook to create a reference to the input element.</p>
</li>
<li><p>We use the <code>useEffect</code> hook to add an event listener for the specified hotkey.</p>
</li>
<li><p><code>navigator.platform</code> is used to determine if the user is using a mac or windows machine so we know what hotkey to display and listen to.</p>
</li>
<li><p>calling <code>inputRef.current.focus()</code> will focus the input if the hotkey is pressed</p>
</li>
<li><p><code>&lt;kbd /&gt;</code> html element is used to denote textual user input from a keyboard <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd">MDN</a></p>
</li>
<li><p><code>navigator.platform</code> is technically deprecated and you could use <code>navigator.userAgent</code> as an alternative.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Why Neovim is My Text Editor of Choice]]></title><description><![CDATA[As a software engineer, choosing and understanding your text editor is important part of your work, as it impacts your productivity and workflow efficiency. It's like choosing the perfect tool for any trade - you need to know what tool to use and how...]]></description><link>https://blog.codebycorey.com/why-neovim-is-my-text-editor-of-choice</link><guid isPermaLink="true">https://blog.codebycorey.com/why-neovim-is-my-text-editor-of-choice</guid><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[vim]]></category><category><![CDATA[neovim]]></category><category><![CDATA[software development]]></category><category><![CDATA[Text Editors]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Tue, 23 Apr 2024 15:18:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713885389194/7f461b57-3b89-46cc-a3f5-e004012ca6e3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a software engineer, choosing and understanding your text editor is important part of your work, as it impacts your productivity and workflow efficiency. It's like choosing the perfect tool for any trade - you need to know what tool to use and how to use it effectively if you want to excel. For me, I use <a target="_blank" href="https://neovim.io/">Neovim</a> as my editor and I have been using it for a little over a year now.</p>
<h2 id="heading-vim-and-its-benefits">Vim and its Benefits</h2>
<p>Vim is a text editor that has been around for decades. Almost all servers running usually comes with some form of Vim preinstalled. It highly customizable and its popularity stems from serveral benefits:</p>
<h3 id="heading-speed-and-efficiency">Speed and Efficiency</h3>
<p>Vim is designed to be operated entirely with the keyboard, allowing for lightning-fast navigation and editing. Once you've mastered Vim's commands, you can perform tasks much more quickly than with a traditional mouse-driven text editor. Imagine never having to touch your mouse again while programming.</p>
<h3 id="heading-customizability">Customizability</h3>
<p>Vim is highly customizable, allowing users to tailor the editor to their specific needs. Whether it's customizing key bindings, installing plugins, or tweaking settings, Vim can be adapted to suit any workflow.</p>
<h3 id="heading-modal-editing">Modal Editing</h3>
<p>One of Vim's most powerful features is its modal editing system. Vim has different modes for editing, navigation, and command entry, allowing users to perform a wide range of tasks without ever lifting their hands from the keyboard.</p>
<h3 id="heading-extensibility">Extensibility</h3>
<p>Vim's plugin system allows users to extend the editor's functionality in countless ways. Whether it's adding support for new file types, integrating with external tools, or automating common tasks, Vim's extensive plugin ecosystem has you covered.</p>
<h2 id="heading-in-comes-neovim">In comes Neovim</h2>
<p>While Vim is an excellent text editor, it does have some limitations. For instance, it's primarily maintained by a single lead developer. Its built using its own programming language making it harder for users to build and maintain plugins. Enter Neovim, a modern fork of Vim that seeks to address many of its shortcomings while still keeping vim at its core.</p>
<p>Developed in 2014 as a community-driven effort to modernize the Vim codebase, Neovim builds on Vim's features while also introducing several improvements and new capabilities. Some of the key features of Neovim include:</p>
<h3 id="heading-built-in-support-for-lsp">Built in support for LSP</h3>
<p>Language Server Protocol (LSP) is a protocol that allows programming languages to be easily supported across any editor. The LSP enables features such as code highlighting, syntax checking, code completion, inlay hints, type hints. Neovim ships with direct support for LSP making it more performant and easier to maintain vs dealing with external plugins.</p>
<h3 id="heading-improved-plugin-architecture">Improved Plugin Architecture</h3>
<p>Neovim is built and configured using the <a target="_blank" href="https://neovim.io/doc/user/lua.html">Lua</a> programming language vs vimscript. This makes it easier to develop and maintain plugins. The Lua API also allows for more powerful scripting and performance.</p>
<h3 id="heading-actively-maintained-and-developed">Actively Maintained and Developed</h3>
<p>Neovim is developed by a community of contributors and is constantly being improved and updated with new features and bug fixes.</p>
<h2 id="heading-customizing-neovim">Customizing Neovim</h2>
<p>Customization is a crucial aspect of any text editor, as it allows users to tailor the editor to their specific needs and preferences. Neovim takes customization to the next level, offering a wide range of options for configuring the editor to fit your workflow. Customizing your text editor is essential for improving productivity and efficiency.</p>
<h3 id="heading-my-experience-with-customizing-neovim">My Experience with Customizing Neovim</h3>
<p>Personally, customizing Neovim has been a game-changer for me. By tweaking the editor to fit my workflow, I've been able to significantly increase my productivity and focus more on writing code and less on repetitive tasks.</p>
<p>Some of the customizations I've made to Neovim include:</p>
<ul>
<li><p><em>Custom key mappings</em>: I've defined custom key mappings to quickly access commonly used commands and plugins, allowing me to perform tasks more efficiently.</p>
</li>
<li><p><em>Syntax highlighting and theme</em>: I've customized Neovim's syntax highlighting and theme to make it easier to read and navigate my code.</p>
</li>
<li><p><em>Plugin configuration</em>: I've installed and configured several plugins to extend Neovim's functionality, such as auto-completion, code linting, and version control integration, and file navigation.</p>
</li>
</ul>
<p>I also enjoy tinkering around with Neovim and learning about different plugins and workflows other engineers use. Whenever I am not feeling motivated on my projects, its usually something fun to take my mind off of things but still stay in the scope of programming.</p>
<p>You can checkout my git repository where I keep all my configuration here: <a target="_blank" href="https://github.com/codebycorey/vinevim">Vinevim</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I believe every Software Engineer should have a deep understanding of their text editor in order for them to be efficient at writing code. You can accomplish this with any text editor; you just have to put in the work. I chose Neovim as my editor as it is built on top of vim and easily customizable. It gave me a deeper understanding of how editors work under the hood and it improved my speed and efficiency at programming. I highly encourage you to give it a try and experience the journey of Vim and Neovim.</p>
]]></content:encoded></item><item><title><![CDATA[What is a Design System and Why Is It Important for Software Engineering?]]></title><description><![CDATA[Design systems are essential tools for software engineers as they provide a structured framework for maintaining consistency and coherence in product design. By offering clear guidelines and standards, design systems streamline the development proces...]]></description><link>https://blog.codebycorey.com/design-system</link><guid isPermaLink="true">https://blog.codebycorey.com/design-system</guid><category><![CDATA[Design]]></category><category><![CDATA[software development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Design Systems]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Fri, 19 Apr 2024 15:49:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713541912038/92b7a77c-c6fc-4791-8542-991be0cb38a0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Design systems are essential tools for software engineers as they provide a structured framework for maintaining consistency and coherence in product design. By offering clear guidelines and standards, design systems streamline the development process, making it easier for engineers to collaborate with designers and implement design elements seamlessly into their code. These systems promote modular and scalable architecture, enabling engineers to efficiently reuse components, update interfaces, and scale applications over time. Additionally, design systems facilitate accessibility and usability by incorporating best practices for inclusive design, ensuring that software products are accessible to all users. Overall, design systems play a vital role in software engineering by promoting efficiency, consistency, and user satisfaction throughout the development lifecycle.</p>
<h2 id="heading-what-is-a-design-system">What is a Design System?</h2>
<p>A design system serves as a comprehensive set of standards and building blocks aimed at maintaining consistency across your product's design. While design and UI engineering can present challenges, adhering to these established rules can significantly streamline the process and unify the overall design.</p>
<p>When considering a design system, it's helpful to envision it as a hierarchical structure comprising various components:</p>
<h3 id="heading-design-system-container">Design System Container</h3>
<p>At the top of the hierarchy is the design system container, which encapsulates all other components. It serves as the repository for the rules and principles governing your product's design. Here, you store essential elements such as design tokens and documentation outlining implementation guidelines.</p>
<h3 id="heading-component-libraries">Component Libraries</h3>
<p>Nested within the design system container are component libraries, which adhere to the established rules and guidelines. These libraries consist of reusable elements—such as buttons, input fields, and layouts—that are consistently utilized throughout the product. By adhering to the foundational elements, these components ensure uniformity in visual presentation.</p>
<h3 id="heading-foundation-elements">Foundation Elements</h3>
<p>Foundation elements form the basis of your design's aesthetic appeal. This level encompasses aspects such as your color palette, typography, and icons. By leveraging the design tokens and rules defined in the design system container, you can construct themes that seamlessly integrate with your component library, facilitating easy customization and maintenance.</p>
<h2 id="heading-benefits-of-a-design-system-for-software-engineering">Benefits of a Design System for Software Engineering</h2>
<p>In addition to enhancing the user experience and maintaining visual consistency, design systems offer significant advantages for software engineering:</p>
<h3 id="heading-streamlined-development-process">Streamlined Development Process</h3>
<p>Design systems provide a shared language and framework for designers and developers, making it eaiser for collaboration and communication. By establishing clear guidelines and standards, they reduce ambiguity and minimize the need for back-and-forth iterations between design and development teams. This streamlined process accelerates development cycles and ensures faster time-to-market for products.</p>
<h3 id="heading-consistent-implementation">Consistent Implementation</h3>
<p>One of the primary benefits of a design system for software engineering is the promotion of consistent implementation. By encapsulating design rules and principles within component libraries, developers can easily access and reuse pre-defined elements throughout the application. This consistency not only improves code maintainability but also reduces the likelihood of introducing errors or inconsistencies during development.</p>
<h3 id="heading-modular-and-scalable-architecture">Modular and Scalable Architecture</h3>
<p>Design systems encourage a modular and scalable architecture by breaking down complex interfaces into reusable components. This modular approach facilitates code reuse, making it easier to update, extend, and scale applications over time. Additionally, by separating design concerns from business logic, design systems promote a more organized and maintainable codebase, enhancing overall software quality and developer productivity.</p>
<h3 id="heading-improved-accessibility-and-usability">Improved Accessibility and Usability</h3>
<p>By incorporating accessibility best practices into design system components, software engineers can ensure that their products are usable by a diverse range of users, including those with disabilities. Design systems provide guidelines for creating accessible interfaces, such as proper color contrast ratios and keyboard navigation support, enabling developers to build inclusive and user-friendly applications from the ground up.</p>
<h3 id="heading-enhanced-developer-experience">Enhanced Developer Experience</h3>
<p>Design systems contribute to an enhanced developer experience by providing comprehensive documentation, code examples, and tooling support. Developers can easily reference design system guidelines and documentation to understand how to implement various components and adhere to design standards. This helps create an efficient and enjoyable development process and simplies the work required.</p>
]]></content:encoded></item><item><title><![CDATA[Real-time Page Views with Next.js and Supabase]]></title><description><![CDATA[Let's Build our own real-time page view tracker using Next.js as the frontend framework and a Postgres database hosted by Supabase.
One of the best ways to understand how your blog posts are performing is by tracking page views. You can begin to unde...]]></description><link>https://blog.codebycorey.com/page-views-nextjs-supabase</link><guid isPermaLink="true">https://blog.codebycorey.com/page-views-nextjs-supabase</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Thu, 31 Dec 2020 14:25:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1609391348361/lUVGtJi5K.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's Build our own real-time page view tracker using Next.js as the frontend framework and a Postgres database hosted by Supabase.</p>
<p>One of the best ways to understand how your blog posts are performing is by tracking page views. You can begin to understand what posts and information your readers care more about based on number of views. You can then focus your efforts on the more important content.</p>
<p>Many people use tools like <a target="_blank" href="https://analytics.google.com/">Google</a> or <a target="_blank" href="https://usefathom.com/ref/45XEAJ">Fathom</a> Analytics for tracking traffic. With the rise of Ad-Blockers, your traffic collected with these services are not necessarily accurate.</p>
<p>Why not build a page view tracker with your own API routes? The beauty of doing so, ad-blockers will not risk blocking the request without possibly breaking website functionality. This will allow for a more accurate count of page views.</p>
<h2 id="tools-used">Tools Used</h2>
<h3 id="nextjs">Next.js</h3>
<p>We will be using <a target="_blank" href="https://nextjs.org/">Next.js</a> as our frontend framework. It gives us the power of prerendered React.js, serverless API routes, and typescript with minimal configuration.</p>
<h3 id="supabase">Supabase</h3>
<p><a target="_blank" href="https://supabase.io/">Supabase</a> is an open-source alternative to Firebase. They offer an API wrapped Postgres Database with real-time subscriptions.</p>
<h2 id="set-up-your-database-hosted-by-supabase">Set up your database hosted by Supabase</h2>
<p>Sign in or Sign up for Supabase. Then create a new project. You can either use an existing organization (if you have one) or create a new one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609390791204/9DynWGeVx.png" alt="Create a new project in Supabase screenshot" /></p>
<p>Enter your project name, add a <strong>strong</strong> password for your database, and select which region you want your database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609390898027/vYtAQEoAO.png" alt="Set the name and password for your database screenshot" /></p>
<p>Once the database finishes setting up, we need to create a table to store our pages and the total view count. Navigate to the SQL editor and switch to the <code>Query-1</code> tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609390933158/S46VJQ70E.png" alt="SQL Editor and Highlighted Query-1 tab screenshot" /></p>
<p>You can then paste the bellow SQL query and run it to create a new table called <code>pages</code> with columns <code>id</code>, <code>slug</code>, <code>view_count</code>, and <code>updated_at</code>.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> pages (
  <span class="hljs-keyword">id</span> <span class="hljs-built_in">bigint</span> <span class="hljs-keyword">GENERATED</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> PRIMARY <span class="hljs-keyword">KEY</span>,
  slug <span class="hljs-built_in">text</span> <span class="hljs-keyword">UNIQUE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  view_count <span class="hljs-built_in">bigint</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">1</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  updated_at <span class="hljs-built_in">timestamp</span> <span class="hljs-keyword">with</span> <span class="hljs-built_in">time</span> zone <span class="hljs-keyword">DEFAULT</span> timezone(<span class="hljs-string">'utc'</span>::<span class="hljs-built_in">text</span>, <span class="hljs-keyword">now</span>()) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>
);
</code></pre>
<p>We need to run one more query to add a stored procedure to our database. A stored procedure allows us to add or extend functionality to the database.</p>
<p>Let's first breakdown the query below.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">FUNCTION</span> increment_page_view(page_slug <span class="hljs-built_in">TEXT</span>)
<span class="hljs-keyword">RETURNS</span> <span class="hljs-built_in">void</span>
<span class="hljs-keyword">LANGUAGE</span> plpgsql
<span class="hljs-keyword">AS</span> $$
<span class="hljs-keyword">BEGIN</span>
    <span class="hljs-keyword">IF</span> <span class="hljs-keyword">EXISTS</span> (<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">FROM</span> pages <span class="hljs-keyword">WHERE</span> slug=page_slug) <span class="hljs-keyword">THEN</span>
        <span class="hljs-keyword">UPDATE</span> pages
        <span class="hljs-keyword">SET</span> view_count = view_count + <span class="hljs-number">1</span>,
            updated_at = <span class="hljs-keyword">now</span>()
        <span class="hljs-keyword">WHERE</span> slug = page_slug;
    ELSE
        <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">into</span> pages(slug) <span class="hljs-keyword">VALUES</span> (page_slug);
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;
<span class="hljs-keyword">END</span>;
$$;
</code></pre>
<ol>
<li>This adds a function called <code>increment_page_view</code> that has a parameter of <code>page_slug</code>.</li>
<li>It sets the language to <code>plpgsql</code> which is specific to Postgres.</li>
<li>When you trigger the function, it checks to see if a row exists where <code>slug</code> equals the parameter <code>page_slug</code>.</li>
<li>If it <strong>exists</strong>, update the row by adding <code>1</code> to <code>view_count</code> and setting <code>updated_at</code> with <code>now()</code>.</li>
<li>If it does <strong>not exist</strong>, insert a new row with <code>slug</code> equal to <code>page_slug</code>.</li>
</ol>
<p>Now that we know what this function actually does, open a new query tab in the SQL editor. Paste the query in and run it.</p>
<p>Your database should be fully set up to track page views now!</p>
<p>The final thing we need to do is get your API keys located in <strong>API</strong> under <strong>settings</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609390986362/5WoxsbtGl.png" alt="API Keys screenshot" /></p>
<p>Now add them to your next.js project's <code>.env</code> file.</p>
<pre><code class="lang-bash">NEXT_PUBLIC_SUPABASE_URL=             <span class="hljs-comment"># URL</span>
NEXT_PUBLIC_SUPABASE_CLIENT_KEY=      <span class="hljs-comment"># public</span>
SUPABASE_SERVICE_KEY=                 <span class="hljs-comment"># service_role</span>
</code></pre>
<h2 id="adding-the-page-view-feature-to-nextjs">Adding the page view feature to Next.js</h2>
<p>Let's first install some dependencies we will use:</p>
<ol>
<li><code>@supabase/supabase-js</code>: Client to connect and query your database hosted by Supabase.</li>
<li><code>swr</code>: React Hook library for data fetching.</li>
</ol>
<pre><code class="lang-bash">npm install @supabase/supabase-js swr
</code></pre>
<p>Now you can create a file <code>lib/supabase-admin</code> to initialize your Supabase client.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@supabase/supabase-js'</span>;

<span class="hljs-keyword">const</span> supabaseUrl: <span class="hljs-built_in">string</span> = process.env.NEXT_PUBLIC_SUPABASE_URL || <span class="hljs-string">''</span>;
<span class="hljs-keyword">const</span> supabaseServerKey: <span class="hljs-built_in">string</span> = process.env.SUPABASE_SERVICE_KEY || <span class="hljs-string">''</span>;

<span class="hljs-keyword">const</span> SupabaseAdmin = createClient(supabaseUrl, supabaseServerKey);

<span class="hljs-keyword">export</span> { SupabaseAdmin };
</code></pre>
<blockquote>
<p><strong>Important</strong>: Only use your ServerKey on server side logic. You do not want to expose your ServerKey publicly. If you want to use the client on your frontend code, use <code>NEXT_PUBLIC_SUPABASE_CLIENT_KEY</code> instead of <code>SUPABASE_SERVICE_KEY</code></p>
</blockquote>
<p>We will need to create an API route to increment and fetch the page views. <code>/pages/api/views/[slug].ts</code></p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { NextApiRequest, NextApiResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>;
<span class="hljs-keyword">import</span> { SupabaseAdmin } <span class="hljs-keyword">from</span> <span class="hljs-string">'@lib/supabase-admin'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (req: NextApiRequest, res: NextApiResponse) =&gt; {
  <span class="hljs-keyword">if</span> (req.method === <span class="hljs-string">'POST'</span>) {
    <span class="hljs-comment">// Call our stored procedure with the page_slug set by the request params slug</span>
    <span class="hljs-keyword">await</span> SupabaseAdmin.rpc(<span class="hljs-string">'increment_page_view'</span>, { page_slug: req.query.slug });
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
      message: <span class="hljs-string">`Successfully incremented page: <span class="hljs-subst">${req.query.slug}</span>`</span>
    });
  }

  <span class="hljs-keyword">if</span> (req.method === <span class="hljs-string">'GET'</span>) {
    <span class="hljs-comment">// Query the pages table in the database where slug equals the request params slug.</span>
    <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> SupabaseAdmin.from(<span class="hljs-string">'pages'</span>).select(<span class="hljs-string">'view_count'</span>).filter(<span class="hljs-string">'slug'</span>, <span class="hljs-string">'eq'</span>, req.query.slug);

    <span class="hljs-keyword">if</span> (data) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
        total: data[<span class="hljs-number">0</span>]?.view_count || <span class="hljs-literal">null</span>
      });
    }
  }

  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({
    message: <span class="hljs-string">'Unsupported Request'</span>
  });
};
</code></pre>
<p>We can now create a component to fetch and display the page view count. <code>/components/PageViews.tsx</code></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> useSWR <span class="hljs-keyword">from</span> <span class="hljs-string">'swr'</span>;
<span class="hljs-keyword">import</span> { FC } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

interface PageViewsProps {
  <span class="hljs-attr">slug</span>: string;
}

<span class="hljs-keyword">const</span> fetcher = <span class="hljs-keyword">async</span> (input: RequestInfo) =&gt; {
  <span class="hljs-keyword">const</span> res: Response = <span class="hljs-keyword">await</span> fetch(input);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> res.json();
};

<span class="hljs-keyword">const</span> PageViews: FC&lt;PageViewsProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ slug }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { data } = useSWR(<span class="hljs-string">`/api/views/<span class="hljs-subst">${slug}</span>`</span>, fetcher);

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>{data?.total ? `${data.total} views` : `–––`}<span class="hljs-tag">&lt;/&gt;</span></span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PageViews;
</code></pre>
<p>Finally, you can add the logic to your pages to increment pages views and display it. Example: <code>/components/BlogLayout.tsx</code></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { FC, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> PageViews <span class="hljs-keyword">from</span> <span class="hljs-string">'@components/PageViews'</span>;

interface BlogLayoutProps {
  <span class="hljs-attr">slug</span>: string;
}

<span class="hljs-keyword">const</span> BlogLayout: FC&lt;BlogLayoutProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ slug }</span>) =&gt;</span> {
  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetch(<span class="hljs-string">`/api/views/<span class="hljs-subst">${slug}</span>`</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>
    });
  }, [slug]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{slug}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">PageViews</span> <span class="hljs-attr">slug</span>=<span class="hljs-string">{slug}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> BlogLayout;
</code></pre>
<p>Whenever you load your page, it should make a <code>POST</code> request to increment your page views. If you wanted, you could check out the Supabase Table Editor view and see your table update as you increment page views on your website.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609391014077/MHOVEW682.png" alt="Verify your table is populated on Supabase screenshot" /></p>
<p>Bam! You now have a real-time page view tracker for your blog or whatever page you want.</p>
<hr />
<ul>
<li>Supabase <a target="_blank" href="https://supabase.io/docs">Docs</a></li>
<li>Next.js <a target="_blank" href="https://nextjs.org/docs/getting-started">Docs</a></li>
<li>SWR <a target="_blank" href="https://swr.vercel.app/">Docs</a></li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and programming.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Create a Dynamic Sitemap with Next.js]]></title><description><![CDATA[One of the best ways to drive traffic to your website is to have strong Search Engine Optimization (SEO). You can provide search engines with all the URLs for your website using a Sitemap. This allows for easier indexing and more efficient crawling b...]]></description><link>https://blog.codebycorey.com/create-a-dynamic-sitemap-with-nextjs</link><guid isPermaLink="true">https://blog.codebycorey.com/create-a-dynamic-sitemap-with-nextjs</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[SEO]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Fri, 18 Dec 2020 14:30:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1608301799439/rV_e1-C-cV.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the best ways to drive traffic to your website is to have strong Search Engine Optimization (SEO). You can provide search engines with all the URLs for your website using a <strong>Sitemap</strong>. This allows for easier indexing and more efficient crawling by the search engines.</p>
<p>Maintaining a static sitemap can be tedious and more work if your website is always changing. The best solution is to dynamically create one.</p>
<p>Let's check out a couple of ways we can accomplish this.</p>
<h2 id="create-a-sitemap-using-a-script-at-build-time">Create a sitemap using a script at build time</h2>
<p>If all of your content and pages are local in your project, you can easily use a script at build time to create a <code>sitemap.xml</code>.</p>
<p>My blog uses <a target="_blank" href="https://mdxjs.com/">MDX</a> files instead of a CMS, so I do not have to worry about my content changing after build time.</p>
<p>My script uses <code>globby</code> to traverse the file system and return all my routes. First thing we need to do is install it as a dev dependency.</p>
<pre><code><span class="hljs-built_in">npm</span> i -D globby
</code></pre><p>Then we can create the script:</p>
<p><code>scripts/generate-sitemap.js</code></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> globby = <span class="hljs-built_in">require</span>(<span class="hljs-string">'globby'</span>);

<span class="hljs-keyword">const</span> generateSitemap = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// Fetch all routes based on patterns</span>
  <span class="hljs-comment">// Your folder structure might be different so change bellow to match your needs</span>
  <span class="hljs-keyword">const</span> pages = <span class="hljs-keyword">await</span> globby([
    <span class="hljs-string">'pages/**/*.{ts,tsx,mdx}'</span>, <span class="hljs-comment">// All routes inside /pages</span>
    <span class="hljs-string">'_content/**/*.mdx'</span>, <span class="hljs-comment">// All MDX files inside my /_content</span>
    <span class="hljs-string">'!pages/**/[*.{ts,tsx}'</span>, <span class="hljs-comment">// Ignore my dynamic route index Example /pages/blog/[slug].tsx</span>
    <span class="hljs-string">'!pages/_*.{ts,tsx}'</span>, <span class="hljs-comment">// Ignore next.js files</span>
    <span class="hljs-string">'!pages/api'</span>, <span class="hljs-comment">// Ignore API routes</span>
    <span class="hljs-string">'!pages/admin.tsx'</span> <span class="hljs-comment">// Ignore pages not meant to be indexed</span>
  ]);

  <span class="hljs-keyword">const</span> urlSet = pages
    .map(<span class="hljs-function">(<span class="hljs-params">page</span>) =&gt;</span> {
      <span class="hljs-comment">// Remove none route related parts of filename.</span>
      <span class="hljs-keyword">const</span> path = page
        .replace(<span class="hljs-string">'pages'</span>, <span class="hljs-string">''</span>)
        .replace(<span class="hljs-string">'_content'</span>, <span class="hljs-string">''</span>)
        .replace(<span class="hljs-regexp">/(.tsx|.ts)/</span>, <span class="hljs-string">''</span>)
        .replace(<span class="hljs-string">'.mdx'</span>, <span class="hljs-string">''</span>);
      <span class="hljs-comment">// Remove the word index from route</span>
      <span class="hljs-keyword">const</span> route = path === <span class="hljs-string">'/index'</span> ? <span class="hljs-string">''</span> : path;
      <span class="hljs-comment">// Build url portion of sitemap.xml</span>
      <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;url&gt;&lt;loc&gt;https://codebycorey.com<span class="hljs-subst">${route}</span>&lt;/loc&gt;&lt;/url&gt;`</span>;
    })
    .join(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// Add urlSet to entire sitemap string</span>
  <span class="hljs-keyword">const</span> sitemap = <span class="hljs-string">`&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;<span class="hljs-subst">${urlSet}</span>&lt;/urlset&gt;`</span>;

  <span class="hljs-comment">// Create sitemap file</span>
  fs.writeFileSync(<span class="hljs-string">'public/sitemap.xml'</span>, sitemap);
};

<span class="hljs-built_in">module</span>.exports = generateSitemap;
</code></pre>
<p>To run the script at build time, you can create a <code>next.config.js</code> file. This will modify Next.js build process.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> generateSitemap = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./scripts/generate-sitemap'</span>);
<span class="hljs-keyword">const</span> generateRSS = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./scripts/generate-rss'</span>);

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">webpack</span>: <span class="hljs-function">(<span class="hljs-params">config, { isServer }</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (isServer) {
      generateSitemap();
    }
    <span class="hljs-keyword">return</span> config;
  }
};
</code></pre>
<p>Now when you build your website, you should see a freshly created <code>public/sitemap.xml</code>.</p>
<p>Lastly, I recommend adding <code>public/sitemap.xml</code> to your <code>.gitignore</code> since it is a generated file.</p>
<h2 id="create-a-sitemap-using-a-route">Create a sitemap using a route</h2>
<p>You cannot create a <code>sitemap</code> at build time When you are using a content management system (CMS). It might work when you first build your project, but if you push out new content after the build, your <code>sitemap</code> will be outdated.</p>
<p>What we could do is create an API route to fetch the data, and we rewrite the sitemap request to use the API route.</p>
<p>First create the API route:</p>
<p><code>pages/api/sitemap.ts</code></p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-comment">// Fetch data from a CMS.</span>
  <span class="hljs-keyword">const</span> resp = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'MOCK_URL'</span>);
  <span class="hljs-keyword">const</span> externalPosts = <span class="hljs-keyword">await</span> resp.json();

  <span class="hljs-keyword">const</span> routes = externalPosts.map(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> <span class="hljs-string">`/blog/<span class="hljs-subst">${posts.slug}</span>`</span>);
  <span class="hljs-keyword">const</span> localRoutes = [<span class="hljs-string">'/index'</span>, <span class="hljs-string">'/blog'</span>];

  <span class="hljs-keyword">const</span> pages = routes.concat(localRoutes);

  <span class="hljs-keyword">const</span> urlSet = pages
    .map(<span class="hljs-function">(<span class="hljs-params">page</span>) =&gt;</span> {
      <span class="hljs-comment">// Remove none route related parts of filename.</span>
      <span class="hljs-keyword">const</span> path = page
        .replace(<span class="hljs-string">'pages'</span>, <span class="hljs-string">''</span>)
        .replace(<span class="hljs-string">'_content'</span>, <span class="hljs-string">''</span>)
        .replace(<span class="hljs-regexp">/(.tsx|.ts)/</span>, <span class="hljs-string">''</span>)
        .replace(<span class="hljs-string">'.mdx'</span>, <span class="hljs-string">''</span>);
      <span class="hljs-comment">// Remove the word index from route</span>
      <span class="hljs-keyword">const</span> route = path === <span class="hljs-string">'/index'</span> ? <span class="hljs-string">''</span> : path;
      <span class="hljs-comment">// Build url portion of sitemap.xml</span>
      <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;url&gt;&lt;loc&gt;https://codebycorey.com<span class="hljs-subst">${route}</span>&lt;/loc&gt;&lt;/url&gt;`</span>;
    })
    .join(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// Add urlSet to entire sitemap string</span>
  <span class="hljs-keyword">const</span> sitemap = <span class="hljs-string">`&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;<span class="hljs-subst">${urlSet}</span>&lt;/urlset&gt;`</span>;

  <span class="hljs-comment">// set response content header to xml</span>
  res.setHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'text/xml'</span>);
  <span class="hljs-comment">// write the sitemap</span>
  res.write(sitemap);
  res.end();
};
</code></pre>
<p>Now we can create a route rewrite to make <code>/sitemap.xml</code> actually call <code>/api/sitemap</code>.</p>
<p>Create <code>next.config.js</code> and add:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-keyword">async</span> rewrites() {
    <span class="hljs-keyword">return</span> [
      {
        <span class="hljs-attr">source</span>: <span class="hljs-string">'/sitemap.xml'</span>,
        <span class="hljs-attr">destination</span>: <span class="hljs-string">'/api/sitemap'</span>
      }
    ];
  }
};
</code></pre>
<p>Now when you navigate to <code>http://localhost:3000/sitemap.xml</code>, you should see your sitemap based on local files and your CMS.</p>
<h2 id="bonus-robotstxt">Bonus: Robots.txt</h2>
<p>One additional thing you can add to your website to improve SEO is a <code>robots.txt</code> (AKA robots exclusion standard). This basically tells search engines which routes they are not allowed to index.</p>
<p>Create <code>public/robots.txt</code> and add</p>
<pre><code><span class="hljs-attribute">User-agent</span>: *
<span class="hljs-attribute">Disallow</span>:

<span class="hljs-attribute">Sitemap</span>: <span class="hljs-attribute">https</span>:<span class="hljs-comment">//your-url.com/sitemap.xml</span>
</code></pre><p>This will tell search engines they are welcome to crawl your entire website.</p>
<p>If you would like to block any pages from being indexed, add it as disallow field.</p>
<pre><code><span class="hljs-attribute">User-agent</span>: *
<span class="hljs-attribute">Disallow</span>: /admin
<span class="hljs-attribute">Disallow</span>: /secret-page

<span class="less"><span class="hljs-attribute">Sitemap</span>: <span class="hljs-attribute">https</span>:<span class="hljs-comment">//your-url.com/sitemap.xml</span></span>
</code></pre><hr />
<ul>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and programming. I am also documenting my journey learning design.</li>
<li>Nest.js <a target="_blank" href="https://nextjs.org/docs/getting-started">Docs</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Prerender React using Next js]]></title><description><![CDATA[When you want to improve your website's performance or search engine optimization (SEO), prerendering your application is a must. Next.js is the perfect framework to add server-side rendering (SSR) or static site generation (SSG) for your React web a...]]></description><link>https://blog.codebycorey.com/prerender-react-using-next-js</link><guid isPermaLink="true">https://blog.codebycorey.com/prerender-react-using-next-js</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[SEO]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Thu, 05 Nov 2020 14:04:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1604548932905/3RuVJgIZI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you want to improve your website's performance or search engine optimization (SEO), prerendering your application is a must. Next.js is the perfect framework to add server-side rendering (SSR) or static site generation (SSG) for your React web app. </p>
<h2 id="the-problems-caused-by-react">The problems caused by React</h2>
<p>Whenever you load the website, the first thing to load is the HTML. The HTML then tells the browser to load additional resources such as styles and JavaScript.</p>
<p>Before JavaScript frameworks became popular, most websites were built with HTML and only enhanced with JavaScript. When the browser loaded the website, most of the content was in the HTML and was instantly displayed while the extra JavaScript loaded later.</p>
<p>With React, nothing gets displayed until all of your JavaScript loads. Your HTML is nearly empty and React injects your content in your HTML with JavaScript. </p>
<p>Example HTML with React.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"theme-color"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"#000000"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span>
      <span class="hljs-attr">content</span>=<span class="hljs-string">"Web site created using create-react-app"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>React App<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">noscript</span>&gt;</span>You need to enable JavaScript to run this app.<span class="hljs-tag">&lt;/<span class="hljs-name">noscript</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"root"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This leads multiple problems:</p>
<ol>
<li>When the browser is loading the JavaScript, the screen is blank because <code>&lt;div id="root"&gt;&lt;/div&gt;</code> does not show anything. Depending on the size of your JavaScript bundle, this could lead to your visitors staring at a white screen for a couple of seconds. This is poor UX.</li>
<li>Most SEO crawlers do not have JavaScript enabled. DuckDuckGo, Google, Bing and any other search engine would not actually know what is on your website since it requires JavaScript to display the content. You will not be ranked at all on Search Engines.</li>
</ol>
<h2 id="server-side-rendering-ssr">Server-Side Rendering (SSR)</h2>
<p>Webpages are generated on your server for every request. Data required by your page can always be fetched allowing your page to always be up to date. </p>
<p>One of the draw backs is a slower time to first byte (TTFB) since the server is waiting for data and generating the page.</p>
<h2 id="static-site-generation-ssg">Static Site Generation (SSG)</h2>
<p>At build time, your app will fetch all the data required and compile it down to static webpages. This provides the best performance and can easily be cached on a CDN. </p>
<p>If your data changes between builds, you might end up causing your webpage to display stale data.</p>
<h2 id="methods-for-prerendering-using-nextjs">Methods for Prerendering using Next.js</h2>
<p>Next.js offers SSR and SSG out of the box. All you have to do is add either <code>getStaticProps</code> for (SSG) or <code>getServerSideProps</code> for (SSR) on your pages.</p>
<h3 id="getstaticprops">getStaticProps</h3>
<p><code>getStaticProps</code> is a server-side function that will only be called at build time. The build will then use the response from <code>getStaticProps</code> to generate a static webpage. </p>
<p>Due to it being called server-side, it is okay to add any sensitive logic or direct calls to databases without worry.</p>
<p>Since stale data is a problem with static generated pages, there is an option you can set to revalidate your static page and rebuild it if data changes. <code>revalidate: 60</code> will check your data every 60 seconds and rebuild the page if needed. </p>
<p>Example Use:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// This function gets called at build time on server-side.</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://.../data'</span>)
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json()

  <span class="hljs-comment">// By returning { props: data }, the Dashboard component</span>
  <span class="hljs-comment">// will receive `data` as a prop at build time</span>
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      data,
    },
    <span class="hljs-comment">// Check if data changes every 60 seconds. </span>
    <span class="hljs-comment">// Rebuild page if different</span>
    <span class="hljs-attr">revalidate</span>: <span class="hljs-number">60</span>, 
  }
}

<span class="hljs-comment">// data will be populated at build time by getStaticProps()</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">Dashboard</span>(<span class="hljs-params">{ data }</span>) </span>{
  <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{data}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>See more details and options from the <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation">official documentation</a></p>
<h3 id="getserversideprops">getServerSideProps</h3>
<p><code>getServerSideProps</code> is similar to <code>getStaticProps</code> but is called every time the page loads instead of at build time. This ensures that all of your initial data is up to date on every load.</p>
<p>Again, due to it being called server-side, it is okay to add any sensitive logic or direct calls to databases without worry.</p>
<p>Since this is called on every load, you do not need to revalidate like <code>getStaticProps</code>. This also leads to a slower load time since you are no longer serving a static file, but have to rebuild on every load.</p>
<p>Example Use:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// This function gets called at build time on server-side.</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getServerSideProps</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://.../data'</span>)
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json()

  <span class="hljs-comment">// By returning { props: data }, the Dashboard component</span>
  <span class="hljs-comment">// will receive `data` as a prop at build time</span>
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      data,
    }
  }
}

<span class="hljs-comment">// data will be populated at build time by getServerSideProps()</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">Dashboard</span>(<span class="hljs-params">{ data }</span>) </span>{
  <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{data}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>See more details and options from the <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering">official documentation</a></p>
<h3 id="best-choice">Best choice</h3>
<p>If performance is your number one priority, SSG is the better solution. You are not waiting for your server to respond and your static generated site can be cached across a CDN.</p>
<p>If you are worried about stale data, you can have a static generated shell with loading states and fetch the data client-side in parallel. You get the benefit of having a fast TTFB while keeping your data up to date.</p>
<hr />
<ul>
<li>Next.js <a target="_blank" href="https://nextjs.org/docs/getting-started">Docs</a></li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and programming. I am also documenting my journey learning design.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Authentication for Next.js using Firebase]]></title><description><![CDATA[On my Next.js project, I wanted to add some authentication. I decided to use Firebase for my user management and data store.
What I needed:

OAuth using Twitter
client-side authentication
Protected pages
server-side authentication


Assumptions: You ...]]></description><link>https://blog.codebycorey.com/authentication-for-nextjs-using-firebase</link><guid isPermaLink="true">https://blog.codebycorey.com/authentication-for-nextjs-using-firebase</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Firebase]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Tue, 13 Oct 2020 13:13:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902230432/YuNpo_QR3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On my Next.js project, I wanted to add some authentication. I decided to use Firebase for my user management and data store.</p>
<p>What I needed:</p>
<ul>
<li>OAuth using Twitter</li>
<li>client-side authentication</li>
<li>Protected pages</li>
<li>server-side authentication</li>
</ul>
<blockquote>
<p>Assumptions: You already have a base Next.js project setup and you created a project in Firebase.</p>
</blockquote>
<h2 id="setup-firebase">Setup Firebase</h2>
<p>Install Firebase's packages</p>
<pre><code class="lang-bash">npm i --save firebase firebase-admin
</code></pre>
<p>Create a <code>env.local</code> file and add all the necessary Firebase keys needed</p>
<pre><code>NEXT<span class="hljs-emphasis">_PUBLIC_</span>FIREBASE<span class="hljs-emphasis">_API_</span>KEY=<span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span>
NEXT<span class="hljs-emphasis">_PUBLIC_</span>FIREBASE<span class="hljs-emphasis">_AUTH_</span>DOMAIN=<span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">**<span class="hljs-emphasis">*
NEXT_PUBLIC_FIREBASE_PROJECT_ID=<span class="hljs-strong">****</span><span class="hljs-strong">****</span>*</span>

FIREBASE<span class="hljs-emphasis">_PRIVATE_</span>KEY=**</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">**<span class="hljs-emphasis">*
FIREBASE_CLIENT_EMAIL=<span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span>*</span>
FIREBASE<span class="hljs-emphasis">_DATABASE_</span>URL=**</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">**<span class="hljs-emphasis">*</span></span>
</code></pre><p>Now we need to create some files to handle connecting to Firebase.</p>
<p><code>lib/firebase.ts</code> - handling OAuth and maintaining authentication.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> firebase <span class="hljs-keyword">from</span> <span class="hljs-string">'firebase/app'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'firebase/auth'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'firebase/functions'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'firebase/firestore'</span>;

<span class="hljs-keyword">if</span> (!firebase.apps.length) {
  firebase.initializeApp({
    apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
    authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
    projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID
  });
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> firebase;
</code></pre>
<p><code>lib/firebase-admin.ts</code> - verifying tokens server side.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> admin <span class="hljs-keyword">from</span> <span class="hljs-string">'firebase-admin'</span>;

<span class="hljs-keyword">if</span> (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert({
      projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
      privateKey: process.env.FIREBASE_PRIVATE_KEY,
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL
    }),
    databaseURL: process.env.FIREBASE_DATABASE_URL
  });
}

<span class="hljs-keyword">const</span> db = admin.firestore();
<span class="hljs-keyword">const</span> auth = admin.auth();

<span class="hljs-keyword">export</span> { db, auth };
</code></pre>
<p><code>lib/db.ts</code> - database queries </p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> firebase <span class="hljs-keyword">from</span> <span class="hljs-string">'../lib/firebase'</span>;

<span class="hljs-keyword">const</span> firestore = firebase.firestore();

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUser</span>(<span class="hljs-params">uid: <span class="hljs-built_in">string</span>, data: <span class="hljs-built_in">any</span></span>) </span>{
  <span class="hljs-keyword">return</span> firestore.collection(<span class="hljs-string">'users'</span>).doc(uid).update(data);
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">uid: <span class="hljs-built_in">string</span>, data: <span class="hljs-built_in">any</span></span>) </span>{
  <span class="hljs-keyword">return</span> firestore
    .collection(<span class="hljs-string">'users'</span>)
    .doc(uid)
    .set({ uid, ...data }, { merge: <span class="hljs-literal">true</span> });
}
</code></pre>
<p>Now we can easily use these lib files to build hooks for maintaining our user's session and auth state.</p>
<h2 id="building-the-auth-hook">Building the Auth Hook.</h2>
<p>I decided to use the context API for handling auth state. This way I can easily access any of the auth variables throughout the application.</p>
<p>First, I created <code>lib/auth.tsx</code>.</p>
<p>Then I set up the context portion of the hook</p>
<pre><code class="lang-jsx">interface AuthContext {
  <span class="hljs-attr">auth</span>: Auth | <span class="hljs-literal">null</span>;
  loading: boolean;
  signInWithTwitter: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt;;
  signOut: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt;;
}

<span class="hljs-comment">// Create context with a default state.</span>
<span class="hljs-keyword">const</span> authContext: Context&lt;AuthContext&gt; = createContext&lt;AuthContext&gt;({
  <span class="hljs-attr">auth</span>: <span class="hljs-literal">null</span>,
  <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">signInWithTwitter</span>: <span class="hljs-keyword">async</span> () =&gt; {},
  <span class="hljs-attr">signOut</span>: <span class="hljs-keyword">async</span> () =&gt; {}
});

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AuthProvider</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">const</span> auth = useProvideAuth();
  <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">{auth}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">authContext.Provider</span>&gt;</span></span>;
}

<span class="hljs-comment">// Helper to easily get auth context within components</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useAuth = <span class="hljs-function">() =&gt;</span> useContext(authContext);
</code></pre>
<p>Time for the more complicated part, implementing <code>useProvideAuth()</code>.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useProvideAuth</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [auth, setAuth] = useState&lt;Auth | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState&lt;boolean&gt;(<span class="hljs-literal">true</span>);

  <span class="hljs-comment">/**
   * Callback function used for firebase.auth.onAuthStateChanged().
   * Takes the user object returned and formats it for my state.
   * We fetch the idToken and append it to my auth state and store it.
   */</span>
  <span class="hljs-keyword">const</span> authStateChanged = <span class="hljs-keyword">async</span> (authState: firebase.User | <span class="hljs-literal">null</span>) =&gt; {
    <span class="hljs-comment">// Formats response into my required state.</span>
    <span class="hljs-keyword">const</span> formattedAuth = formatAuth(authState);
    <span class="hljs-comment">// Fetch firebase auth ID Token.</span>
    formattedAuth.token = <span class="hljs-keyword">await</span> authState.getIdToken();
    <span class="hljs-comment">// Stores auth into state.</span>
    setAuth(formattedAuth);
    <span class="hljs-comment">// Sets loading state to false.</span>
    setLoading(<span class="hljs-literal">false</span>);
  };

  <span class="hljs-comment">/**
   * Callback function used for response from firebase OAuth.
   * Store user object returned in firestore.
   * @param firebase User Credential
   */</span>
  <span class="hljs-keyword">const</span> signedIn = <span class="hljs-keyword">async</span> (resp: firebase.auth.UserCredential) =&gt; {
    <span class="hljs-comment">// Format user into my required state.</span>
    <span class="hljs-keyword">const</span> storeUser = formatAuth(resp.user);
    <span class="hljs-comment">// firestore database function</span>
    createUser(storeUser.uid, storeUser);
  };

  <span class="hljs-comment">/**
   * Callback for when firebase signOut.
   * Sets auth state to null and loading to true.
   */</span>
  <span class="hljs-keyword">const</span> clear = <span class="hljs-function">() =&gt;</span> {
    setAuth(<span class="hljs-literal">null</span>);
    setLoading(<span class="hljs-literal">true</span>);
  };

  <span class="hljs-comment">/**
   * Triggers firebase Oauth for twitter and calls signIn when successful.
   * sets loading to true.
   */</span>
  <span class="hljs-keyword">const</span> signInWithTwitter = <span class="hljs-function">() =&gt;</span> {
    setLoading(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">return</span> firebase.auth().signInWithPopup(<span class="hljs-keyword">new</span> firebase.auth.TwitterAuthProvider()).then(signedIn);
  };

  <span class="hljs-comment">/**
   * Calls firebase signOut and with clear callback to reset state.
   */</span>
  <span class="hljs-keyword">const</span> signOut = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> firebase.auth().signOut().then(clear);
  };

  <span class="hljs-comment">/**
   * Watches for state change for firebase auth and calls the handleUser callback
   * on every change.
   */</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> unsubscribe = firebase.auth().onAuthStateChanged(authStateChanged);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> unsubscribe();
  }, []);

  <span class="hljs-comment">// returns state values and callbacks for signIn and signOut.</span>
  <span class="hljs-keyword">return</span> {
    auth,
    loading,
    signInWithTwitter,
    signOut
  };
}
</code></pre>
<h2 id="using-auth-hook">Using Auth Hook</h2>
<p>I added <code>AuthProvider</code> to my <code>pages/_app.tsx</code>.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { AppProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/app'</span>;
<span class="hljs-keyword">import</span> { AuthProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'../lib/auth'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'../styles/globals.css'</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">MyApp</span>(<span class="hljs-params">{ Component, pageProps }: AppProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AuthProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">AuthProvider</span>&gt;</span></span>
  );
}
</code></pre>
<p>Now we can use the <code>AuthContext</code> in our pages.</p>
<p>We can add a sign in button on <code>pages/index.tsx</code>. If we are authenticated, we can display a link and sign out button.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'../lib/auth'</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">'next/link'</span>;
<span class="hljs-keyword">import</span> { useEffect } <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">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { auth, signOut, signInWithTwitter } = useAuth();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {auth ? (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/dashboard'</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> signOut()}&gt;Sign Out<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ) : (
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> signInWithTwitter()}&gt;Sign In<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>
<p>I want my dashboard route to be protected with auth. If user is not authenticated, it will redirect back to the index page.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/router'</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'../lib/auth'</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">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { auth, loading, signOut } = useAuth();

  <span class="hljs-keyword">const</span> router = useRouter();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// If auth is null and we are no longer loading</span>
    <span class="hljs-keyword">if</span> (!auth &amp;&amp; !loading) {
      <span class="hljs-comment">// redirect to index</span>
      router.push(<span class="hljs-string">'/'</span>);
    }
  }, [auth, loading]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Dashboard: Hello World<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      {auth &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> signOut()}&gt;Sign Out<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="server-side-authentication">Server-Side Authentication</h2>
<p>Server-side authentication would be handled by passing the id token from the client to the API. The API would then verify the token on every request.</p>
<p>Let's first create a fetch util that passes the token. <code>util/fetcher.ts</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> fetcher = <span class="hljs-keyword">async</span> (url: <span class="hljs-built_in">string</span>, token: <span class="hljs-built_in">string</span>) =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(url, {
    method: <span class="hljs-string">'GET'</span>,
    headers: <span class="hljs-keyword">new</span> Headers({ <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>, token }),
    credentials: <span class="hljs-string">'same-origin'</span>
  });

  <span class="hljs-keyword">return</span> res.json();
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> fetcher;
</code></pre>
<p>We can then verify the token on the API route using <code>firebase-admin</code>.</p>
<p>API Route: <code>pages/api/user.ts</code></p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { NextApiRequest, NextApiResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>;
<span class="hljs-keyword">import</span> { auth } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../lib/firebase-admin'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (req: NextApiRequest, res: NextApiResponse) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { uid } = <span class="hljs-keyword">await</span> auth.verifyIdToken(req.headers.token);

    res.status(<span class="hljs-number">200</span>).json({ uid });
  } <span class="hljs-keyword">catch</span> (error) {
    res.status(<span class="hljs-number">401</span>).json({ error });
  }
};
</code></pre>
<p>We can now make the API call to fetch the user data inside our dashboard page. I use the <code>useSWR</code> hook for handling API calls.</p>
<p><code>pages/dashboard.tsx</code></p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/router'</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> useSWR <span class="hljs-keyword">from</span> <span class="hljs-string">'swr'</span>;
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'../lib/auth'</span>;
<span class="hljs-keyword">import</span> fetcher <span class="hljs-keyword">from</span> <span class="hljs-string">'../util/fetcher'</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">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { auth, loading, signOut } = useAuth();

  <span class="hljs-keyword">const</span> router = useRouter();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!auth &amp;&amp; !loading) {
      router.push(<span class="hljs-string">'/'</span>);
    }
  }, [auth, loading]);

  <span class="hljs-keyword">const</span> { data } = useSWR(auth ? [<span class="hljs-string">'/api/user'</span>, auth.token] : <span class="hljs-literal">null</span>, fetcher);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Dashboard: Hello World<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      {auth &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> signOut()}&gt;Sign Out<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>
      )}
      {data &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{data}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>I now have working authentication for my web app using Firebase.</p>
<ul>
<li>The user can sign in using Twitter's OAuth.</li>
<li>It creates the user and stores it in Firebase.</li>
<li>I have a protected route with a redirect if the user is not authenticated.</li>
<li>I have a protected endpoint that verifies the user's token on ever request.</li>
</ul>
<p>Here is the <a target="_blank" href="https://github.com/CodeByCorey/nextjs-firebase-auth">Repository</a> with working code for the article.</p>
<p>It might not be the best solution, but it gets the job done.</p>
<hr />
<ul>
<li>Next.js <a target="_blank" href="https://nextjs.org/docs/getting-started">Docs</a></li>
<li>Firebase <a target="_blank" href="https://firebase.google.com/docs/">Docs</a></li>
<li>SWR <a target="_blank" href="https://swr.vercel.app/">Docs</a></li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and programming. I am also documenting my journey learning design.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Dark Mode Toggle Using Tailwind CSS]]></title><description><![CDATA[Tailwind recently released an experimental setting that enables dark mode styles. It allows you to add a prefix to specific classes to only enable the styles when dark mode is enabled. It's currently labeled experimental and future versions might hav...]]></description><link>https://blog.codebycorey.com/dark-mode-toggle-using-tailwind-css</link><guid isPermaLink="true">https://blog.codebycorey.com/dark-mode-toggle-using-tailwind-css</guid><category><![CDATA[General Programming]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Sat, 03 Oct 2020 02:27:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902189484/ohVARYWCV.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Tailwind recently released an experimental setting that enables dark mode styles. It allows you to add a prefix to specific classes to only enable the styles when dark mode is enabled. It's currently labeled experimental and future versions might have breaking changes so use at your own risk.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white text-black dark:bg-black dark:text-white"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h2 id="enabling-dark-mode">Enabling Dark Mode</h2>
<p>To enable dark mode add the <code>experimental</code> object to your tailwind.config.js with <code>darkModeVariant: true</code>.</p>
<pre><code class="lang-js"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">experimental</span>: {
    <span class="hljs-attr">darkModeVariant</span>: <span class="hljs-literal">true</span>
  },
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>The default strategy is using a media query and will set dark mode based on the operating system performances. This is great for keeping your website themed the way the user's computer is set but your user cannot easily toggle your websites theme without changing their operating system settings.</p>
<p>Tailwind offers a setting to use a class instead of the media query so you can toggle dark mode by adding and removed the <code>.dark</code> CSS class.</p>
<pre><code class="lang-js"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">experimental</span>: {
    <span class="hljs-attr">darkModeVariant</span>: <span class="hljs-literal">true</span>
  },
  <span class="hljs-attr">dark</span>: <span class="hljs-string">'class'</span>,
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Example of using dark mode in your HTML</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dark"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white text-black dark:bg-black dark:text-white"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<h2 id="toggle-button-using-react">Toggle Button using React</h2>
<p>I added a toggle button on my website using a simple react hook. The button with switch between</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> [ darkMode, setDarkMode ] = useState(<span class="hljs-literal">false</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">{</span>`<span class="hljs-attr">w-full</span> <span class="hljs-attr">md:h-screen</span> ${<span class="hljs-attr">darkMode</span> ? '<span class="hljs-attr">dark</span>' <span class="hljs-attr">:</span> ''}`}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-100 dark:bg-gray-900 dark:text-white"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setDarkMode(!darkMode)}&gt;
            {darkMode ? (
              <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-8 h-8 md:w-10 md:h-10"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
                <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
                <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">{2}</span>
                <span class="hljs-attr">d</span>=<span class="hljs-string">"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"</span>
                /&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
            ) : (
              <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-8 h-8 md:w-10 md:h-10"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
                <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
                <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">{2}</span>
                <span class="hljs-attr">d</span>=<span class="hljs-string">"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"</span>
                /&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">svg</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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>Now I have a button that will switch between a sun and a moon icon based on whether dark mode is true or not. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1601691182598/YPJuxUFwT.gif" alt="toggle button.gif" /></p>
<hr />
<ul>
<li>Documentation for dark mode <a target="_blank" href="https://github.com/tailwindlabs/tailwindcss/pull/2279">Tailwind CSS</a></li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and programming. I am also documenting my journey learning design.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Importance of typography]]></title><description><![CDATA[It may not feel like typography is that important, but it can have a major effect on your reader's experience. Typography is essentially the art and techniques for displaying your written words.
Typography can have positive and negative effects on yo...]]></description><link>https://blog.codebycorey.com/importance-of-typography</link><guid isPermaLink="true">https://blog.codebycorey.com/importance-of-typography</guid><category><![CDATA[Design]]></category><category><![CDATA[Web Design]]></category><category><![CDATA[typography]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[Blogging]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Tue, 29 Sep 2020 17:34:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902132000/-R_qWynlW.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It may not feel like typography is that important, but it can have a major effect on your reader's experience. Typography is essentially the art and techniques for displaying your written words.</p>
<p>Typography can have positive and negative effects on your words without you realizing it.</p>
<p>Positive Effects of good typography:</p>
<ul>
<li>Keep the readers more engaged.</li>
<li>Guide the readers focus to the more important content.</li>
<li>Persuade your readers based on mood and feeling.</li>
</ul>
<p>Negative Effects of bad typography:</p>
<ul>
<li>Disinterested in boring text.</li>
<li>Exhaustion when it's difficult to read.</li>
<li>Misleading or hard to follow.</li>
</ul>
<p>Your reader's attention is the most important part of producing content. Think about how busy your reader might be. They have their own life and challenges. When they are offering their precious time and attention, you should make it easy for them. The less effort it takes for them to read, the more likely they will continue to read and provide their attention. Typography is not for you, it is for your reader.</p>
<h2 id="rules-i-learned-for-better-typography">Rules I learned for better typography</h2>
<ol>
<li><p>Try to always use <strong>justify left</strong>.</p>
<p> Most of your readers probably read left to right, top to bottom. Justify left will make this easier. Justify center only works with headings and things like invitations. It's also difficult to read when you have large paragraphs with difference sized rows. You should just stay away from forced justify. The spacing between words can leave rivers of spaces and look less appealing.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1601399989428/BzeQ_6r6k.png" alt="typo-justify.png" /></p>
</li>
<li><p>Use one font until you know how your fonts work together.</p>
<p> For every font you add, you need to understand more about typography. It is definitely possible to pick fonts that are complementary, but can be difficult. The best way to have them complementary is to use fonts with a different classification: serif, sans-serif, slab-serif.</p>
</li>
<li><p>Stay away from the goofy and monospaced fonts for body text.</p>
<p> Goofy can make it feel out of place unless it is follows your design. Monospaced fonts should be used for code blocks.</p>
</li>
<li><p>When choosing font weights, always skip a level. <code>fine/medium</code>, <code>light/bold</code>, <code>medium/extra-bold</code>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1601400717048/IsEcq7h1u.png" alt="typo-contrast.png" /></p>
</li>
<li><p>Always use multiples when choosing sizes for fonts and rules. <code>2x/4x</code>, <code>2x/8x</code>, <code>4x/8x</code>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1601400092958/mC1Mv2-T4.png" alt="typo-multiples.png" /></p>
</li>
<li><p>For body text on the web, stick to <code>15-25px</code>.</p>
</li>
<li><p>The line space should be roughly <code>120-145%</code>.</p>
</li>
<li><p>When using ALL CAPS, add extra letter spacing (<code>5-12%</code>).</p>
</li>
<li><p>Do not be afraid to play around with it. Just make small incremental changes.</p>
</li>
<li><p>Do not be afraid of negative (empty) space. Give your reader some room to breathe.</p>
</li>
</ol>
<p>I am still learning and will continue to add more rules to my list. Maybe we can compare and share as you learn also.</p>
<h3 id="sources">Sources</h3>
<ul>
<li>Butterick's Practical Typography: <a target="_blank" href="https://practicaltypography.com/">Web Book</a></li>
<li>The Futur's Typography Manual: <a target="_blank" href="https://www.youtube.com/watch?v=QrNi9FmdlxY">Youtube Video</a></li>
</ul>
<hr />
<ul>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and programming. I am also documenting my journey learning design.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Defeating Flash of Unstyled Text (FOUT)]]></title><description><![CDATA[I noticed when I loaded my personal site without cache, my text would first display without the correct font. It eventually restyle using the original font after everything loaded.

I was originally using a NPM package called typeface-roboto to load ...]]></description><link>https://blog.codebycorey.com/defeating-flash-of-unstyled-text-fout</link><guid isPermaLink="true">https://blog.codebycorey.com/defeating-flash-of-unstyled-text-fout</guid><category><![CDATA[General Programming]]></category><category><![CDATA[CSS]]></category><category><![CDATA[Web Design]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[typography]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Thu, 24 Sep 2020 19:24:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902161141/eNw-eVscD.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I noticed when I loaded my personal site without cache, my text would first display without the correct font. It eventually restyle using the original font after everything loaded.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1600973770807/HEhsPBgji.gif" alt="font-flash.gif" /></p>
<p>I was originally using a NPM package called <code>typeface-roboto</code> to load my font. This was awesome because I wanted to host the fonts myself instead of pulling from a CDN. I realized the styles included in the package was the reason for my Flash of Unstyled Text (FOUT).</p>
<p>I looked through the code inside the package and checked out the CSS files. Here is an example of how they were adding the font.</p>
<pre><code class="lang-css"><span class="hljs-keyword">@font-face</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Roboto'</span>;
  <span class="hljs-attribute">font-style</span>: normal;
  <span class="hljs-attribute">font-display</span>: swap; <span class="hljs-comment">/* Line Causing FOUT */</span>
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;
  <span class="hljs-attribute">src</span>:
    <span class="hljs-built_in">local</span>(<span class="hljs-string">'Roboto Medium '</span>),
    <span class="hljs-built_in">local</span>(<span class="hljs-string">'Roboto-Medium'</span>),
    <span class="hljs-built_in">url</span>(<span class="hljs-string">'/fonts/roboto-latin-500.woff2'</span>) <span class="hljs-built_in">format</span>(<span class="hljs-string">'woff2'</span>),
    <span class="hljs-built_in">url</span>(<span class="hljs-string">'/fonts/roboto-latin-500.woff'</span>) <span class="hljs-built_in">format</span>(<span class="hljs-string">'woff'</span>);
}
</code></pre>
<p>I noticed the <code>@font-face</code> styles were using <code>font-display: swap;</code>. When <code>swap</code> is set, the browser will give a very short time to load the font before it uses a fallback. Once the font is fully loaded, it will convert back to the expected font. This option is great from a performance standpoint but might affect user experience. If you set <code>font-display: block</code>, this will increase the amount of time the browser will have to fetch your fonts before it starts displaying anything on the page. <code>block</code> sacrifices performance for user experience.</p>
<h2 id="my-solution">My Solution</h2>
<p>I pulled all the font files into my project instead of using the NPM package. Then inside my CSS, I loaded the fonts using <code>font-display: block</code>;</p>
<pre><code class="lang-css"><span class="hljs-keyword">@font-face</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Roboto'</span>;
  <span class="hljs-attribute">font-style</span>: normal;
  <span class="hljs-attribute">font-display</span>: block; <span class="hljs-comment">/* No longer causing FOUT */</span>
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;
  <span class="hljs-attribute">src</span>:
    <span class="hljs-built_in">local</span>(<span class="hljs-string">'Roboto Medium '</span>),
    <span class="hljs-built_in">local</span>(<span class="hljs-string">'Roboto-Medium'</span>),
    <span class="hljs-built_in">url</span>(<span class="hljs-string">'/fonts/roboto-latin-500.woff2'</span>) <span class="hljs-built_in">format</span>(<span class="hljs-string">'woff2'</span>),
    <span class="hljs-built_in">url</span>(<span class="hljs-string">'/fonts/roboto-latin-500.woff'</span>) <span class="hljs-built_in">format</span>(<span class="hljs-string">'woff'</span>);
}
</code></pre>
<p>Since my websites design is heavily focused on typography. I was willing to sacrifice some performance to give my fonts a little more time to load before anything is displayed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1600973956338/KNgGDNZ9i.gif" alt="no-flash.gif" /></p>
<hr />
<ul>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech, programming, and working from home.</li>
<li><a target="_blank" href="https://coreyodonnell.tech">Personal Website</a> in case you wanted to check it out yourself</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Fathom Analytics: Privacy-Focused Website Analytics]]></title><description><![CDATA[The Rise of Privacy
Stop stealing your visitors' personal information. Recently, there has been a growing trend of people starting to take their privacy more serious. They realize that they are being tracked across the entire web of internet. Imagine...]]></description><link>https://blog.codebycorey.com/fathom-analytics-privacy-focused-website-analytics</link><guid isPermaLink="true">https://blog.codebycorey.com/fathom-analytics-privacy-focused-website-analytics</guid><category><![CDATA[General Programming]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[analytics]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Wed, 23 Sep 2020 16:41:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902108654/jnY8QAfkl.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="the-rise-of-privacy">The Rise of Privacy</h2>
<p>Stop stealing your visitors' personal information. Recently, there has been a growing trend of people starting to take their privacy more serious. They realize that they are being tracked across the entire web of internet. Imagine someone following you all day watching every little thing you do without you knowing. They watch you while you are working, reading, and playing. Tools such as Google Analytics make it easier for you to have more insight on your website but this comes at the expense of invading your user's privacy. Countries are adding laws where website need to be more transparent on their tracking or will be fined. Great news is there are tools that are privacy oriented to help keep your visitors' privacy intact while still collecting valuable metrics.</p>
<h2 id="introducing-fathom-analytics">Introducing Fathom Analytics</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1600877745436/k3V_n4IR9.png" alt="fathom-intro" /></p>
<p>I have been using <a target="_blank" href="https://usefathom.com/ref/45XEAJ">Fathom Analytics</a> (Referral link for $10 off) for my blog and side projects for the past couple months. It has been a great tool to collect metrics without invading my visitors' privacy.</p>
<p>Some great benefits using Fathom:</p>
<ul>
<li>You don't need to display cookie notices as they do not use cookies, nor track invasive data.</li>
<li>No personal information is collected from my visitors. Privacy is their number one focus.</li>
<li>Ability to use a custom domain for tracking to prevent ad-blockers from blocking the script from loading.</li>
<li>Extremely light-weight script to keep load time minimal.</li>
<li>Add unlimited websites to my account.</li>
<li>Add monitoring for uptime on my websites with multiple forms of notification.</li>
<li>The dashboard is simple and easy to use. It displays everything I need with a clean chart and tables.</li>
<li>Their support has always been quick to respond and always helpful.</li>
<li>They also pledge 2% of their revenues to green initiatives.</li>
</ul>
<h3 id="usage">Usage</h3>
<p>Using Fathom is extremely simple. All you need to do is add a new site in your Fathom settings. This will generate a <code>Site ID</code> and provide you with fathom script to be added to your HTML.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Fathom - beautiful, simple website analytics --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.usefathom.com/script.js"</span> <span class="hljs-attr">site</span>=<span class="hljs-string">"SITE_ID"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-comment">&lt;!-- / Fathom --&gt;</span>
</code></pre>
<p>They also have JavaScript package <a target="_blank" href="https://github.com/derrickreimer/fathom-client">fathom-client</a> to make it easy to add it to your JavaScript Frameworks. I use the client inside my Next.js projects.</p>
<p>Example Use in Next.js:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/router'</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> Fathom <span class="hljs-keyword">from</span> <span class="hljs-string">'fathom-client'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
  <span class="hljs-keyword">const</span> router = useRouter()

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Initialize Fathom when the app loads</span>
    Fathom.load(<span class="hljs-string">'SITE_ID'</span>, {
      <span class="hljs-attr">includedDomains</span>: [<span class="hljs-string">'YOUR_SITE_URL'</span>]
    })

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onRouteChangeComplete</span>(<span class="hljs-params"></span>) </span>{
      Fathom.trackPageview()
    }
    <span class="hljs-comment">// Record a pageview when route changes</span>
    router.events.on(<span class="hljs-string">'routeChangeComplete'</span>, onRouteChangeComplete)

    <span class="hljs-comment">// Unassign event listener</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      router.events.off(<span class="hljs-string">'routeChangeComplete'</span>, onRouteChangeComplete)
    }
  }, [])

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span></span>
}
</code></pre>
<h2 id="be-a-part-of-the-growing-privacy-movement">Be a part of the growing privacy movement</h2>
<p>Be a part of the growing movement of improving privacy. I know privacy is important for me. Ditch the invasive tracking tools such as Google Analytics. Switch to something that still gives you metrics awhile still providing privacy. Try <a target="_blank" href="https://usefathom.com/ref/45XEAJ">Fathom Analytics</a>! (Referral link for $10 off).</p>
<hr />
<ul>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech, programming, and working from home.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Utility-First CSS Is All the Rage]]></title><description><![CDATA[Introduction to styles
When you are building a website, you typically want to style your HTML. You could use inline styles on your HTML elements.
<article style="background-color: purple; text-align:center; width: 750px;">
    <h1 style="color: red; ...]]></description><link>https://blog.codebycorey.com/utility-first-css-is-all-the-rage</link><guid isPermaLink="true">https://blog.codebycorey.com/utility-first-css-is-all-the-rage</guid><category><![CDATA[CSS]]></category><category><![CDATA[CSS Frameworks]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Fri, 18 Sep 2020 15:16:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902081324/fdz1H3dRs.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="introduction-to-styles">Introduction to styles</h2>
<p>When you are building a website, you typically want to style your HTML. You could use inline styles on your HTML elements.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background-color: purple; text-align:center; width: 750px;"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: red; font-weight: bold; font-size: 72px;"</span>&gt;</span>Inline Style Header<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: green;"</span>&gt;</span>I am styling this html with inline styles.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: blue;"</span>&gt;</span>I am styling this html with inline styles.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
</code></pre>
<p>Great, now we have some styles on our HTML! Inline styles do the job but it can quickly grow into something difficult to manage. There are a couple of problems too. If you open this website on a device that is smaller than 750px, your webpage will be too wide. You also cannot create different hover styles for your anchor tag.</p>
<h2 id="css-to-the-rescue">CSS to the rescue</h2>
<p>Cascading Style Sheets (CSS) added more features and simplified styling. It brought the ability to combine a list of styles under a single class name.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
  <span class="hljs-selector-class">.article-container</span> {
    <span class="hljs-attribute">background-color</span>: purple;
    <span class="hljs-attribute">text-align</span>:center;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">750px</span>;
  }
  <span class="hljs-selector-class">.article-header</span> {
    <span class="hljs-attribute">color</span>: red;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">72px</span>;
  }
  <span class="hljs-selector-class">.article-body</span> {
    <span class="hljs-attribute">color</span>: green;
  }
  <span class="hljs-selector-class">.article-link</span> {
    <span class="hljs-attribute">color</span>: blue;
  }
  <span class="hljs-selector-class">.article-link</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: grey;
  }
  <span class="hljs-keyword">@media</span> <span class="hljs-keyword">only</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">500px</span>) {
  <span class="hljs-comment">/* For mobile phones: */</span>
  <span class="hljs-selector-class">.article-container</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article-container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article-header"</span>&gt;</span>Inline Style Header<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article-body"</span>&gt;</span>I am styling this html with inline styles.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article-link"</span>&gt;</span>I am styling this html with inline styles.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
</code></pre>
<p>One of great things about CSS classes is that you can share styles across multiple elements. If you change the styles under the class, it will affect all elements using that class. It also adds new ways to style elements such as pseudo classes: hover, focus, active, and more. Pseudo classes allow you to have different styles on an element based the status of the element. CSS also introduced responsive design using media queries that helped produce a better design experience across multiple devices.</p>
<h2 id="common-pitfalls-of-css">Common pitfalls of CSS</h2>
<p>CSS has some great benefits but it can have some problems. As your project continues to grow, you start to add more and more styles. Your style sheets can become complicated quickly, especially if you are sharing classes across multiple elements or components. If you make a change to a class, it is hard to tell how many components the change might affect. When this happens, you start to move to less shared classes and more specialized classes. Now you run into the problem with having redundant styles causing your stylesheets to grow. This will increase your load time and make it harder to understand all the code inside the stylesheet.</p>
<h2 id="introduction-to-utility-first-css">Introduction to Utility-First CSS</h2>
<p>Imagine cleaner inline styles that has all the features of CSS. That is essentially what Utility-First CSS is. Your CSS framework is set up with atomic css classes. Every class created adds one style and you have a class for every possible style you will need.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
  <span class="hljs-selector-class">.bg-purple</span> {
    <span class="hljs-attribute">background-color</span>: purple;
  }
  <span class="hljs-selector-class">.text-center</span> {
    <span class="hljs-attribute">text-align</span>:center;
  }
  <span class="hljs-selector-class">.w-50</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">50%</span>;
  }
  <span class="hljs-selector-class">.text-red</span> {
    <span class="hljs-attribute">color</span>: red;
  }
  <span class="hljs-selector-class">.font-bold</span> {
    <span class="hljs-attribute">font-weight</span>: bold;
  }
  <span class="hljs-selector-class">.font-large</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">72px</span>;
  }
  <span class="hljs-selector-class">.text-green</span> {
    <span class="hljs-attribute">color</span>: green;
  }
  <span class="hljs-selector-class">.text-blue</span> {
    <span class="hljs-attribute">color</span>: blue;
  }
  <span class="hljs-selector-class">.hover-text-grey</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: grey;
  }
  <span class="hljs-keyword">@media</span> <span class="hljs-keyword">only</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">500px</span>) {
  <span class="hljs-comment">/* For mobile phones: */</span>
  <span class="hljs-selector-class">.mobile-w-100</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-purple text-center w-50 mobile-w-100"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red font-bold font-large"</span>&gt;</span>Inline Style Header<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-green"</span>&gt;</span>I am styling this html with inline styles.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-blue hover-text-grey"</span>&gt;</span>I am styling this html with inline styles.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
</code></pre>
<p>As you can see from the example, all the CSS classes are responsible for one style. Once you have a utility first framework, you rarely have to write custom CSS. This means your CSS will no longer grow and you can focus on styling your elements instead of thinking about CSS. Maintaining HTML is way easier than maintaining CSS classes.</p>
<h2 id="recommended-framework">Recommended Framework</h2>
<p>If you are now looking for a utility-first CSS framework, there are a couple of different options. <a target="_blank" href="https://getbootstrap.com/">Bootstrap</a> has a decent amount of utility classes but focuses a lot on predefined components. I would recommend <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a>. Tailwind is developed to be full utility first and is constantly improving. Their documentation is easy to follow and the creator also made <a target="_blank" href="https://tailwindcss.com/course">full tutorial</a> on building a website using tailwind.</p>
<h3 id="installing-tailwind-css">Installing Tailwind CSS</h3>
<p>To install tailwind, I would recommend checking out the <a target="_blank" href="https://tailwindcss.com/docs/installation">documentation</a>. You need to have an NPM project setup so you can use their NPM package.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Using npm</span>
npm install tailwindcss
</code></pre>
<p>You need to add the tailwind directives to your CSS</p>
<pre><code class="lang-css"><span class="hljs-comment">/* styles.css */</span>
<span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>If you want to add any customization to your tailwind framework, you will need to set up the config using</p>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p>This will generate the <code>tailwind.config.js</code> file where you can modify, extend, and configure custom styles.</p>
<p>Now when you want to compile your CSS, you can use the Tailwind CLI or postCSS CLI. The command for Tailwind CLI is simple but you lose out on some added benefits of postCSS.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># CLI</span>
npx tailwindcss build styles.css -o output.css
</code></pre>
<p>For postCSS, you will need to install <code>autoprefixer</code> and <code>postcss-cli</code></p>
<pre><code><span class="hljs-built_in">npm</span> i postcss-cli autoprefixer
</code></pre><p>Next, create a <code>postcss.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-built_in">require</span>(<span class="hljs-string">"tailwindcss"</span>),
    <span class="hljs-built_in">require</span>(<span class="hljs-string">"autoprefixer"</span>)
  ],
};
</code></pre>
<p>You can run your postcss to compile your CSS for use in your HTML using</p>
<pre><code><span class="hljs-selector-tag">postcss</span> <span class="hljs-selector-tag">styles</span><span class="hljs-selector-class">.css</span> <span class="hljs-selector-tag">-o</span> <span class="hljs-selector-tag">styles</span><span class="hljs-selector-class">.compiled</span><span class="hljs-selector-class">.css</span>
</code></pre><h2 id="conclusion">Conclusion</h2>
<p>I have really enjoyed using Utility-First CSS. I have never been a fan of writing CSS. Class names are hard to manage and time-consuming thinking through what to call them. I can clearly see what styles are being applied to my HTML elements, and they are not hidden behind a single class name. I think there is a reason Tailwind CSS has grown in popularity so quickly. Do you use Utility-First CSS?</p>
<hr />
<ul>
<li>Documentation for <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a></li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech, programming, and working from home.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Building a VSCode Extension: Part Four]]></title><description><![CDATA[One of the of most important things to make this extension function is to figure out the best way to have the React.js app communicate with the extension framework. After reading the docs and playing around, it was fairly simple using VS Codes messag...]]></description><link>https://blog.codebycorey.com/building-a-vscode-extension-part-four</link><guid isPermaLink="true">https://blog.codebycorey.com/building-a-vscode-extension-part-four</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Visual Studio Code]]></category><category><![CDATA[React]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Mon, 14 Sep 2020 14:02:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902830378/aZA095uyn.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the of most important things to make this extension function is to figure out the best way to have the React.js app communicate with the extension framework. After reading the docs and playing around, it was fairly simple using VS Codes message API.</p>
<h2 id="passing-messages-with-vs-codes-api">Passing Messages with VS Code's API</h2>
<p>VS Code offers a special API Object inside their webview by calling <code>acquireVsCodeApi</code> inside your JavaScript. The API object has a <code>postMessage()</code> function that can be used to send messages back to the extension's backend. You can subscribe to messages in the backend using the <code>panel.webview.onDidReceiveMessage()</code> function.</p>
<p>Example of sending a message when script is loaded in Webview App.tsx</p>
<pre><code class="lang-TypeScript"><span class="hljs-comment">// Add typedef for acquireVsCodeApi</span>
<span class="hljs-keyword">declare</span> <span class="hljs-keyword">const</span> acquireVsCodeApi: <span class="hljs-built_in">Function</span>;
<span class="hljs-comment">// Fetch the api object</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> vscodeApi = acquireVsCodeApi();
vscodeApi.postMessage(<span class="hljs-string">'React App Loaded'</span>)
</code></pre>
<p>You can then verify your extension caught the message using:</p>
<pre><code class="lang-TypeScript">panel.webview.onDidReceiveMessage(<span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'MESSAGE'</span>, message))
</code></pre>
<p>Now that we can send messages to the VS Code backend, we need to figure out how to send messages back to the webview and catch it. You can easily send a message using <code>panel.webview.postMessage()</code> function which is similar to how we sent the message from the webview. Instead of using the VSCodeAPI to catch the message in the webview, you actually add an event listener on the window object for <code>message</code>.</p>
<p>Sending the message from the VS Code backend after react app loads:</p>
<pre><code class="lang-TypeScript">panel.webview.onDidReceiveMessage(<span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (message === <span class="hljs-string">'React App Loaded'</span>) {
        panel.webview.postMessage(<span class="hljs-string">'Extension Knows React is ready'</span>);
    }
})
</code></pre>
<p>Webview listening for a message from the VS Code backend in App.tsx:</p>
<pre><code class="lang-TypeScript"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'message'</span>, <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'CAUGHT THE MESSAGE'</span>, message));
</code></pre>
<p>You should now see a <code>console.log()</code> with the caught message.</p>
<h2 id="cleaning-up-the-react-code">Cleaning up the React Code</h2>
<p>I decided to create a lib service that wraps the VS Code API. I can add more type checking to the API and simplify cleanup of <code>eventListeners</code>.</p>
<pre><code class="lang-TypeScript"><span class="hljs-keyword">declare</span> <span class="hljs-keyword">const</span> acquireVsCodeApi: <span class="hljs-built_in">Function</span>;

<span class="hljs-keyword">interface</span> VSCodeApi {
    getState: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">any</span>;
    setState: <span class="hljs-function">(<span class="hljs-params">newState: <span class="hljs-built_in">any</span></span>) =&gt;</span> <span class="hljs-built_in">any</span>;
    postMessage: <span class="hljs-function">(<span class="hljs-params">message: <span class="hljs-built_in">any</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">class</span> VSCodeWrapper {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> vscodeApi: VSCodeApi = acquireVsCodeApi();

    <span class="hljs-comment">/**
     * Send message to the extension framework.
     * @param message
     */</span>
    <span class="hljs-keyword">public</span> postMessage(message: <span class="hljs-built_in">any</span>): <span class="hljs-built_in">void</span> {
        <span class="hljs-built_in">this</span>.vscodeApi.postMessage(message);
    }

    <span class="hljs-comment">/**
     * Add listener for messages from extension framework.
     * @param callback called when the extension sends a message
     * @returns function to clean up the message eventListener.
     */</span>
    <span class="hljs-keyword">public</span> onMessage(callback: <span class="hljs-function">(<span class="hljs-params">message: <span class="hljs-built_in">any</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>): <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span> {
        <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'message'</span>, callback);
        <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">'message'</span>, callback);
    }
}

<span class="hljs-comment">// Singleton to prevent multiple fetches of VsCodeAPI.</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> VSCodeAPI: VSCodeWrapper = <span class="hljs-keyword">new</span> VSCodeWrapper();
</code></pre>
<p>I can now subscribe to messages using <code>useEffect</code> inside of my App.tsx:</p>
<pre><code class="lang-TypeScript"><span class="hljs-keyword">import</span> React, { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>;

<span class="hljs-keyword">import</span> { VSCodeAPI } <span class="hljs-keyword">from</span> <span class="hljs-string">'./lib/VSCodeAPI'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">return</span> VSCodeAPI.onMessage(<span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'app'</span>, message));
    });
    <span class="hljs-keyword">return</span> (
        &lt;h1&gt;Hello World&lt;/h1&gt;
    );
}
</code></pre>
<h2 id="next-steps">Next Steps</h2>
<p>Now that we can pass data between the view and the backend, we can start working on actual functionality. I need to look through the VS Code documentation on how to create a custom editor to generate and modify the <code>todo.md</code> file. I want to add Tailwind CSS to the front end for styles and create views for displaying and submitting todos.</p>
]]></content:encoded></item><item><title><![CDATA[Managing Node.js with Volta]]></title><description><![CDATA[I recently wrote a blog post about how I use NVM to manage my node version. Someone commented on the post saying I should look into Volta
What is Volta
Volta is a command line tool used to manage your Node.js. It is built using Rust and is shipped as...]]></description><link>https://blog.codebycorey.com/managing-nodejs-with-volta</link><guid isPermaLink="true">https://blog.codebycorey.com/managing-nodejs-with-volta</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Rust]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Fri, 11 Sep 2020 13:07:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902027316/ry7KQnLX6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently wrote a blog post about how I use NVM to <a target="_blank" href="https://blog.coreyodonnell.tech/managing-my-node-versions">manage my node version</a>. Someone commented on the post saying I should look into <a target="_blank" href="https://volta.sh/">Volta</a></p>
<h2 id="what-is-volta">What is Volta</h2>
<p>Volta is a command line tool used to manage your Node.js. It is built using Rust and is shipped as a static binary that can be ran on Windows and all *nix shells. The goal is to make sure every developer working on the project seamlessly has the same tools and versions installed.</p>
<h3 id="installation">Installation</h3>
<p>The installation is simple.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># install Volta</span>
curl https://get.volta.sh | bash
</code></pre>
<p>The script installs the binary in <code>~/.volta</code> and adds <code>~/.volta/bin</code> to your systems path inside of your <code>~/.bash_profile</code>, <code>~/.profile</code>, and <code>~/.bashrc</code>.</p>
<pre><code><span class="hljs-built_in">export</span> VOLTA_HOME=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.volta"</span>
<span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$VOLTA_HOME</span>/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre><p>Now you can start using Volta to manage Node.js.</p>
<h3 id="using-volta">Using Volta</h3>
<p>You can easily install node using</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install node</span>
volta install node
<span class="hljs-comment"># or you can install a specific version</span>
volta install node@12
</code></pre>
<p>Now node should be available to use whenever you open your terminal.</p>
<p>If you want to set Volta to always load a specific version of node for your active package you can use <code>pin</code>.</p>
<pre><code class="lang-bash">volta pin node@12.18.3
</code></pre>
<p>This command will store your pinned version in your <code>package.json</code>.</p>
<pre><code class="lang-json"><span class="hljs-string">"volta"</span>: {
  <span class="hljs-attr">"node"</span>: <span class="hljs-string">"12.18.3"</span>
}
</code></pre>
<p>Every time you navigate to your project, Volta will automatically set your active node version to whatever is pinned.</p>
<p>You can even install and pin global packages like yarn using Volta to make sure everyone on your team is using the same version for their global packages.</p>
<pre><code class="lang-bash">volta install yarn
volta pin yarn
</code></pre>
<h2 id="how-does-volta-compare-to-nvm">How does Volta compare to NVM?</h2>
<p>NVM is just a node version manager. It only handles installing different versions of node. You can also set a default version of node to load whenever you open your terminal. Volta handles node versions and can set a default version to load also.</p>
<p>When opening a terminal, NVM usually takes about 0.5 to 2 seconds to source in bash if you have a default node version set. Volta does not seem to add any load time.</p>
<p>You can pin node version for your projects using both tools. NVM uses a <code>.nvmrc</code> file and Volta adds a key to your <code>package.json</code>. Volta can also pin versions for global NPM packages used for the project.</p>
<p>NVM does not automatically switch your active node version to your pinned version. You have to run <code>nvm use</code> or install another package call <code>AVN</code>. AVN usually takes 2 to 5 seconds to switch node versions. Volta does it automatically and usually takes less than a second.</p>
<h2 id="what-tool-will-i-be-using">What tool will I be using?</h2>
<p>Even though I have been using NVM for almost 4 years, I think Volta takes the crown. I plan to use Volta for managing all my Node.js needs from now on. The speed and simplicity of the tool just makes it the better choice. NVM, I am thankful you for all the headaches you have saved me in the past but I think it is time to move on.</p>
<hr />
<ul>
<li><a target="_blank" href="https://docs.volta.sh/guide/">Volta Docs</a></li>
<li><a target="_blank" href="https://github.com/nvm-sh/nvm/blob/master/README.md">NVM Docs</a></li>
<li>Previous post about using <a target="_blank" href="https://blog.coreyodonnell.tech/managing-my-node-versions">NVM to manage node version</a></li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and working from home.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Using Hashnode's API for Blog Previews]]></title><description><![CDATA[I recently decided to host my blog on Hashnode so I could spend more time writing and less managing the code. I still wanted to display previews and links to my most recent posts on my portfolio website. Luckily, Hashnode offers a GraphQL API where I...]]></description><link>https://blog.codebycorey.com/using-hashnodes-api-for-blog-previews</link><guid isPermaLink="true">https://blog.codebycorey.com/using-hashnodes-api-for-blog-previews</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Hashnode]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Tue, 08 Sep 2020 13:36:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602901995960/ps-HR4wnL.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently decided to host my blog on Hashnode so I could spend more time writing and less managing the code. I still wanted to display previews and links to my most recent posts on my portfolio website. Luckily, Hashnode offers a GraphQL API where I can fetch my most recent posts.</p>
<h2 id="the-api">The API</h2>
<p>You can access the API playground and docs at <a target="_blank" href="https://api.hashnode.com/">api.hashnode.com</a>. This allows you to develop your query and give you the exact response you want. After reading the docs, I built a query to give me everything I needed to display a preview on my portfolio page.</p>
<pre><code>{
  <span class="hljs-keyword">user</span>(username: "CodeByCorey") {
    <span class="hljs-keyword">publication</span> {
      posts(page: <span class="hljs-number">0</span>) {
        slug
        title
        brief
        coverImage
        replyCount
        totalReactions
      }
    }
  }
}
</code></pre><ul>
<li><code>user(username: "CodeByCorey")</code>: Query for my user<ul>
<li><code>publication</code>: Contains all information for my blog publication<ul>
<li><code>posts(page: 0)</code>: Returns all the posts on the first page<ul>
<li><code>slug</code>: So I can create a link to the blog post</li>
<li><code>title</code>: To display the title of my post</li>
<li><code>brief</code>: Is a small snippet of the text in the post</li>
<li><code>coverImage</code>: So I can show the cover image in the preview</li>
<li><code>replyCount</code>: The number of comments on the post</li>
<li><code>totalReactions</code>: Total number of reactions on my post</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="using-the-query">Using the Query</h3>
<p>Now that I have the query, it's time to use it to fetch the data. I created a new lib file in my Next.js app called <code>posts.ts</code>. I used fetch to make the API call and passed the query to the body of the request.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> query: <span class="hljs-built_in">string</span> = <span class="hljs-string">`
  {
    user(username: "CodeByCorey") {
      publication {
        posts(page: 0) {
          slug
          title
          brief
          coverImage
          replyCount
          totalReactions
        }
      }
    }
  }
`</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> fetchPosts = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> resp: Response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://api.hashnode.com'</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({ query }),
  })
  <span class="hljs-keyword">const</span> hashnodeResponse = <span class="hljs-keyword">await</span> resp.json();
  <span class="hljs-keyword">return</span> hashnodeResponse.data.user.publication.posts;
};
</code></pre>
<p>I wanted to only display the last three posts. I added another function to slice the posts to limit the response. This</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> fetchThreeMostRecentPost = <span class="hljs-keyword">async</span>() =&gt; {
  <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> fetchPosts();
  <span class="hljs-keyword">return</span> posts.slice(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>);
}
</code></pre>
<p>Inside my container component, I used the Next.js <code>getStaticProps</code> function to fetch the posts and pass them to the props of my component. I added the revalidate setting to automatically regenerate my HTML when I create a new post on Hashnode.</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">getStaticProps</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> fetchThreeMostRecentPosts();
  <span class="hljs-keyword">return</span> {
    props: {
      posts
    },
    revalidate: <span class="hljs-number">60</span>
  };
}
</code></pre>
<p>Now that all the data is being fetched and passed to the props, It was now time to style my components. I have been using Tailwind CSS for my portfolio website. Here is the <code>RecentBlogPosts</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">RecentBlogPosts</span>(<span class="hljs-params">{ posts }: Props</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"container mx-auto py-12 text-gray-800"</span>&gt;
      &lt;h2 className=<span class="hljs-string">"text-center text-2xl md:text-4xl pb-6"</span>&gt;Recent Blog Posts&lt;/h2&gt;
      &lt;div className=<span class="hljs-string">"flex flex-wrap justify-center"</span>&gt;
        {posts.map(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> (
          &lt;a key={index} href={<span class="hljs-string">`https://blog.coreyodonnell.tech/<span class="hljs-subst">${post.slug}</span>`</span>} className=<span class="hljs-string">"md:w-2/3 lg:w-1/3 px-5 my-2"</span>&gt;
            &lt;BlogPreview post={post} /&gt;
          &lt;/a&gt;
        ))}
      &lt;/div&gt;
      &lt;div className=<span class="hljs-string">"flex flex-wrap justify-center"</span>&gt;
        &lt;a
          className=<span class="hljs-string">"text-green-500 font-semibold hover:text-gray-800 py-4 px-4 rounded"</span>
          href=<span class="hljs-string">"https://blog.coreyodonnell.tech/"</span>
        &gt;
          View all posts
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>BlogPreview:</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">BlogPreview</span>(<span class="hljs-params">{ post }: Props</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"h-full border-2 border-gray-200 rounded-lg flex flex-col justify-between"</span>&gt;
      &lt;div className=<span class="hljs-string">"w-full"</span>&gt;
        &lt;img className=<span class="hljs-string">"lg:h-48 md:h-36 w-full object-cover object-center"</span> src={post.coverImage} alt=<span class="hljs-string">"blog"</span> /&gt;
        &lt;div className=<span class="hljs-string">"p-6"</span>&gt;
          &lt;h1 className=<span class="hljs-string">"title-font text-lg font-medium text-gray-900 mb-3"</span>&gt;{post.title}&lt;/h1&gt;
          &lt;p className=<span class="hljs-string">"leading-relaxed mb-3 text-gray-600"</span>&gt;{post.brief}&lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div className=<span class="hljs-string">"flex items-center flex-wrap p-6"</span>&gt;
        &lt;span className=<span class="hljs-string">"text-indigo-500 inline-flex items-center md:mb-2 lg:mb-0"</span>&gt;
          Learn More
          &lt;svg
            className=<span class="hljs-string">"w-4 h-4 ml-2"</span>
            viewBox=<span class="hljs-string">"0 0 24 24"</span>
            stroke=<span class="hljs-string">"currentColor"</span>
            strokeWidth=<span class="hljs-string">"2"</span>
            fill=<span class="hljs-string">"none"</span>
            strokeLinecap=<span class="hljs-string">"round"</span>
            strokeLinejoin=<span class="hljs-string">"round"</span>
          &gt;
            &lt;path d=<span class="hljs-string">"M5 12h14"</span>&gt;&lt;/path&gt;
            &lt;path d=<span class="hljs-string">"M12 5l7 7-7 7"</span>&gt;&lt;/path&gt;
          &lt;/svg&gt;
        &lt;/span&gt;
        &lt;span className=<span class="hljs-string">"text-gray-600 mr-3 inline-flex items-center lg:ml-auto md:ml-0 ml-auto leading-none text-sm pr-3 py-1 border-r-2 border-gray-300"</span>&gt;
          &lt;svg className=<span class="hljs-string">"w-4 h-4 mr-1"</span> xmlns=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> fill=<span class="hljs-string">"none"</span> viewBox=<span class="hljs-string">"0 0 24 24"</span> stroke=<span class="hljs-string">"currentColor"</span>&gt;
            &lt;path
              strokeLinecap=<span class="hljs-string">"round"</span>
              strokeLinejoin=<span class="hljs-string">"round"</span>
              strokeWidth={<span class="hljs-number">2</span>}
              d=<span class="hljs-string">"M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"</span>
            /&gt;
          &lt;/svg&gt;
          {post.totalReactions}
        &lt;/span&gt;
        &lt;span className=<span class="hljs-string">"text-gray-600 inline-flex items-center leading-none text-sm"</span>&gt;
          &lt;svg className=<span class="hljs-string">"w-4 h-4 mr-1"</span> xmlns=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> fill=<span class="hljs-string">"none"</span> viewBox=<span class="hljs-string">"0 0 24 24"</span> stroke=<span class="hljs-string">"currentColor"</span>&gt;
            &lt;path
              strokeLinecap=<span class="hljs-string">"round"</span>
              strokeLinejoin=<span class="hljs-string">"round"</span>
              strokeWidth={<span class="hljs-number">2</span>}
              d=<span class="hljs-string">"M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"</span>
            /&gt;
          &lt;/svg&gt;
          {post.replyCount}
        &lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>The result after styling my components:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1599530492695/SpYR5UpX0.png" alt="hashnode_api_blog_preview.png" /></p>
<hr />
<ul>
<li>Hashnode API - <a target="_blank" href="https://api.hashnode.com/">api.hashnode.com</a></li>
<li>Next.js Docs - <a target="_blank" href="https://nextjs.org/">https://nextjs.org/</a></li>
<li>You can check out my <a target="_blank" href="https://coreyodonnell.tech">portfolio page</a> - <a target="_blank" href="https://github.com/CodeByCorey/coreyodonnell.tech">Source Code</a>(work in progress)</li>
<li>Follow me on <a target="_blank" href="https://twitter.com/CodeByCorey">Twitter</a> for random posts about tech and working from home.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Managing my node versions]]></title><description><![CDATA[Working on multiple projects at a time, I typically have to switch what version of node I am running. For work, the front end code is using NodeJS v10 and our API micro-services are using NodeJS v8. My personal projects are using NodeJS v12, and some...]]></description><link>https://blog.codebycorey.com/managing-my-node-versions</link><guid isPermaLink="true">https://blog.codebycorey.com/managing-my-node-versions</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[dev tools]]></category><category><![CDATA[programming]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Wed, 02 Sep 2020 18:26:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602901965026/zUr0ENhHI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Working on multiple projects at a time, I typically have to switch what version of node I am running. For work, the front end code is using NodeJS v10 and our API micro-services are using NodeJS v8. My personal projects are using NodeJS v12, and sometimes I like to play around with bleeding edge features on the newest and latest version.</p>
<p>Since I am constantly requiring different node versions, I need a tool to make this process easy.</p>
<h2 id="methods-that-will-not-work">Methods that will not work</h2>
<p>I currently develop my personal projects using Ubuntu. I could easily install node using <code>apt</code>.</p>
<pre><code><span class="hljs-attribute">sudo</span> apt install nodejs
</code></pre><p>This won't work for me. How would I switch between all the different node versions? You can install specific versions using apt but you cannot switch easily. It would also take more work to lock your apt version down so whenever you perform an update across your system, it doesn't update node without you realizing it.</p>
<p>I also do not think we should be installing NodeJS with <code>sudo</code> permissions. You are now giving NodeJS full control over your computer. NPM installs third-party modules and it can contain any script the provider wants. How can you trust a third-party script with full control? Unless you read every module and submodule's code before you install it, there might be something malicious. I would rather not take the risk and stay away from <code>sudo</code>.</p>
<p>You could download the binary directly from the NodeJS website and place it in your <code>~/.local/bin</code> folder and make sure that folder is in your <code>$PATH</code> but you still cannot manage different versions easily.</p>
<h2 id="nvm-to-the-rescue">NVM to the rescue!</h2>
<p><a target="_blank" href="https://github.com/nvm-sh/nvm/blob/master/README.md">Official Docs</a></p>
<p>I have been using NVM to managing my node version for the past 4 years and it's one of the first things I install on a new computer.</p>
<p>Installation is fairly simple. They provide a one line copy and paste to install and set up your environment.</p>
<pre><code class="lang-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
<span class="hljs-comment"># Version might be difference since article was published</span>
</code></pre>
<p>When the above script runs, it installs nvm in <code>~/.nvm</code> and adds the initializer for your terminal (<code>~/.bash_profile</code>, <code>~/.zshrc</code>, <code>~/.profile</code>, or <code>~/.bashrc</code>).</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Initializer</span>
[ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span>
</code></pre>
<p>After the script finishes, all you need to do is restart your terminal and it should work. You can verify by typing <code>nvm --version</code> or <code>command -v nvm</code>.</p>
<p>If it does not work, nvm provides troubleshooting steps for <a target="_blank" href="https://github.com/nvm-sh/nvm/blob/master/README.md#troubleshooting-on-linux">Linux</a> and <a target="_blank" href="https://github.com/nvm-sh/nvm/blob/master/README.md#troubleshooting-on-macos">macOS</a></p>
<h3 id="using-nvm-after-its-installed">Using NVM after its installed</h3>
<p>Once you have nvm installed and working, it's easy to manage node.</p>
<p>You can easily install whatever version you want using <code>nvm install</code></p>
<pre><code class="lang-bash"><span class="hljs-comment"># For most recent version of nodejs v12</span>
nvm install 12
<span class="hljs-comment"># You can be more specific</span>
nvm install 12.18.3
</code></pre>
<p>To switch <code>node</code> version you can call <code>nvm use</code></p>
<pre><code class="lang-bash"><span class="hljs-comment"># To enable node 12</span>
nvm use 12
<span class="hljs-comment"># to enable node 8</span>
nvm use 8
</code></pre>
<p>If your folder contains a <code>.nvmrc</code> file you can just run <code>nvm use</code> and it will pull the version from <code>.nvmrc</code>.</p>
<p>To trigger <code>nvm use</code> automatically, you can use another package called <a target="_blank" href="https://github.com/wbyoung/avn">AVN</a> or a <a target="_blank" href="https://github.com/nvm-sh/nvm/blob/master/README.md#automatically-call-nvm-use">lightweight script</a> offered by nvm.</p>
<h3 id="complaints-of-nvm-being-slow">Complaints of NVM being slow</h3>
<p>Every time you start a new terminal, NVM automatically sources your default node version. When this process runs, it calls <code>npm config get prefix</code>. This command has some performance issues and there has been a <a target="_blank" href="https://github.com/nvm-sh/nvm/issues/2212">ticket</a> create explaining the reason.</p>
<p>There are two solutions that help with terminal performance. You can remove the default node version using <code>nvm unalias default</code> or when you source nvm you add <code>--no-use</code>.</p>
<pre><code class="lang-bash">[ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh --no-use"</span>
</code></pre>
<p>NVM will no longer source node at startup and you will have to manually run <code>nvm use</code> before running anything with node.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I have been using NVM for the past 4 years. It has given me all the tools I need to update and manage my node versions safely. The installation is simple and straightforward. I have never had any problems with it and use it almost daily.</p>
]]></content:encoded></item><item><title><![CDATA[Building a VSCode Extension: Part Three]]></title><description><![CDATA[Now that I have a blank VS Code extension set up and working, I want to start building on it.
Adding some Code formatting configs
The Yeoman template for VS Code Extension does not have any formatting configs that I typically use for my projects.
I m...]]></description><link>https://blog.codebycorey.com/building-a-vscode-extension-part-three</link><guid isPermaLink="true">https://blog.codebycorey.com/building-a-vscode-extension-part-three</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Visual Studio Code]]></category><category><![CDATA[React]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Tue, 01 Sep 2020 16:16:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902802805/aaFMfclle.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that I have a blank VS Code extension set up and working, I want to start building on it.</p>
<h2 id="adding-some-code-formatting-configs">Adding some Code formatting configs</h2>
<p>The Yeoman template for VS Code Extension does not have any formatting configs that I typically use for my projects.</p>
<p>I make sure to always have an <code>.editorconfig</code> file. <a target="_blank" href="https://editorconfig.org/">EditorConfig</a> is used to help maintain consistent coding styles for whitespace across everyone's text editors and IDEs. Here is an example I typically use on my typescript projects.</p>
<pre><code><span class="hljs-comment"># .editorconfig</span>
<span class="hljs-comment"># top-most EditorConfig file</span>
<span class="hljs-attr">root</span> = <span class="hljs-literal">true</span>

<span class="hljs-comment"># Unix-style newlines with a newline ending every file</span>
<span class="hljs-section">[*]</span>
<span class="hljs-attr">end_of_line</span> = lf
<span class="hljs-attr">insert_final_newline</span> = <span class="hljs-literal">true</span>
<span class="hljs-attr">trim_trailing_whitespace</span> = <span class="hljs-literal">true</span>

<span class="hljs-comment"># Matches multiple files with brace expansion notation</span>
<span class="hljs-comment"># Set default charset</span>
<span class="hljs-section">[*.{js,jsx,ts,tsx}]</span>
<span class="hljs-attr">charset</span> = utf-<span class="hljs-number">8</span>
<span class="hljs-attr">indent_style</span> = space
<span class="hljs-attr">indent_size</span> = <span class="hljs-number">4</span>

<span class="hljs-comment"># Matches the exact files either package.json or .travis.yml</span>
<span class="hljs-section">[package.json]</span>
<span class="hljs-attr">indent_style</span> = space
<span class="hljs-attr">indent_size</span> = <span class="hljs-number">2</span>
</code></pre><p><a target="_blank" href="https://prettier.io/">Prettier</a> adds even more code formatting. It really helps create a consistent code style. Every developer has a different way of implementing code. Having a consistent style is important for open source. Here is the <code>.prettierrc</code> config I am using for my extension.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"printWidth"</span>: <span class="hljs-number">160</span>,
    <span class="hljs-attr">"trailingComma"</span>: <span class="hljs-string">"none"</span>,
    <span class="hljs-attr">"tabWidth"</span>: <span class="hljs-number">4</span>,
    <span class="hljs-attr">"useTabs"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"semi"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"singleQuote"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"jsxSingleQuote"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"bracketSpacing"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>I work on multiple projects that all require a different node version. I use <a target="_blank" href="https://github.com/nvm-sh/nvm#installing-and-updating">NVM</a> along with <a target="_blank" href="https://github.com/wbyoung/avn">AVN</a> to auto switch my node version depending on which repository I am in. Example <code>.node-version</code> file used in this repository.</p>
<pre><code><span class="hljs-selector-tag">v12</span><span class="hljs-selector-class">.18</span><span class="hljs-selector-class">.3</span>
</code></pre><p>With some consistency added to the code base, it's time to work on the react app.</p>
<h2 id="bootstrapping-react">Bootstrapping React</h2>
<p>Creating a brand new react app is fairly simple using the <a target="_blank" href="https://create-react-app.dev/">create-react-app</a> tool.</p>
<p>I knew I wanted the app in a subdirectory called webview in my extension. First I navigated to the <code>src</code> directory and then used <code>create-react-app</code> to set up an empty react app. I used the typescript template since I wanted this entire extension using typescript including the react portion.</p>
<pre><code>cd src/
npx <span class="hljs-keyword">create</span>-react-app webview <span class="hljs-comment">--template typescript</span>
</code></pre><p>Now I just wanted to verify everything was set up and working.</p>
<pre><code>cd webview/
npm run <span class="hljs-keyword">start</span>
</code></pre><p>It failed with this error...</p>
<pre><code>There might be a problem with the project dependency tree.
It is likely <span class="hljs-keyword">not</span> a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App <span class="hljs-keyword">requires</span> a dependency:

  <span class="hljs-string">"eslint"</span>: <span class="hljs-string">"^6.6.0"</span>

Don<span class="hljs-number">'</span>t <span class="hljs-keyword">try</span> to install it manually: your package manager does it automatically.
However, a different version of eslint was detected higher up in the tree:

  /home/CodeByCorey/workspace/vscode-todo-task-manager/node_modules/eslint (version: <span class="hljs-number">7.7</span><span class="hljs-number">.0</span>)

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore <span class="hljs-keyword">this</span> check, add SKIP_PREFLIGHT_CHECK=<span class="hljs-literal">true</span> to an .env file in your project.
That will permanently disable <span class="hljs-keyword">this</span> message but you might encounter other issues.

To fix the dependency tree, <span class="hljs-keyword">try</span> following the steps below in the exact order:

  <span class="hljs-number">1.</span> Delete package-lock.json (<span class="hljs-keyword">not</span> package.json!) <span class="hljs-keyword">and</span>/<span class="hljs-keyword">or</span> yarn.lock in your project folder.
  <span class="hljs-number">2.</span> Delete node_modules in your project folder.
  <span class="hljs-number">3.</span> Remove <span class="hljs-string">"eslint"</span> from dependencies <span class="hljs-keyword">and</span>/<span class="hljs-keyword">or</span> devDependencies in the package.json file in your project folder.
  <span class="hljs-number">4.</span> Run npm install <span class="hljs-keyword">or</span> yarn, depending on the package manager you use.

In most cases, <span class="hljs-keyword">this</span> should be enough to fix the problem.
If <span class="hljs-keyword">this</span> has <span class="hljs-keyword">not</span> helped, there are a few other things you can <span class="hljs-keyword">try</span>:

  <span class="hljs-number">5.</span> If you used npm, install yarn (http:<span class="hljs-comment">//yarnpkg.com/) and repeat the above steps with it instead.</span>
     This may help because npm has known issues with package hoisting which may get resolved in <span class="hljs-built_in">future</span> versions.

  <span class="hljs-number">6.</span> Check <span class="hljs-keyword">if</span> /home/CodeByCorey/workspace/vscode-todo-task-manager/node_modules/eslint is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  <span class="hljs-number">7.</span> Try running npm ls eslint in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed eslint.

If nothing <span class="hljs-keyword">else</span> helps, add SKIP_PREFLIGHT_CHECK=<span class="hljs-literal">true</span> to an .env file in your project.
That would permanently disable <span class="hljs-keyword">this</span> preflight check in <span class="hljs-keyword">case</span> you want to proceed anyway.

P.S. We know <span class="hljs-keyword">this</span> message is <span class="hljs-keyword">long</span> but please read the steps above :-) We hope you find them helpful!
</code></pre><p>I looked in the root package.json for my VS Code extension and it is using <code>eslint@7</code> and react-scrips requires <code>eslint@6</code>. Due to how yarn/npm handles packages, my react app was not installing <code>eslint</code> at <code>v6</code> because yarn already saw it installed at v7 at the root of the project.</p>
<p>The easiest solution I used was to downgrade my extension's <code>eslint</code> version on my root project.</p>
<pre><code><span class="hljs-comment"># navigate back to the root of the project</span>
<span class="hljs-built_in">cd</span> ../../
yarn add -D eslint@6
<span class="hljs-built_in">cd</span> src/webview
yarn start
</code></pre><p>Boom! It worked and opened my app in the browser at <code>http://localhost:3000</code></p>
<p>I moved the <code>extension.ts</code> into its own directory to help keep the webview and extension separate.</p>
<pre><code>mkdir -p src/<span class="hljs-keyword">extension</span>
mv src/<span class="hljs-keyword">extension</span>.ts src/<span class="hljs-keyword">extension</span>/<span class="hljs-keyword">extension</span>.ts
</code></pre><p>and changed the <code>main</code> key on the package.json to use the new folder structure</p>
<pre><code><span class="hljs-string">"main"</span>: <span class="hljs-string">"./dist/extension/extension.js"</span>
</code></pre><h2 id="how-do-i-get-vs-code-to-open-it">How do I get VS Code to open it??</h2>
<p>The react app is working in my browser but how do I make VS Code display it?</p>
<p>First thing I did was add the VS Code commands that would open the react app inside package.json</p>
<pre><code class="lang-json"><span class="hljs-string">"activationEvents"</span>: [
    <span class="hljs-string">"onCommand:vscode-task-manager.openTodoManager"</span>
],
<span class="hljs-string">"contributes"</span>: {
    <span class="hljs-attr">"commands"</span>: [
        {
            <span class="hljs-attr">"command"</span>: <span class="hljs-string">"vscode-task-manager.openTodoManager"</span>,
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Todo Manager"</span>
        }
    ]
}
</code></pre>
<p>Inside <code>extension.ts</code> I replace the helloWorld command with my new command.
Using the <a target="_blank" href="https://code.visualstudio.com/api/extension-guides/webview">Webview docs</a> I figured out how to open a panel with HTML.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> vscode <span class="hljs-keyword">from</span> <span class="hljs-string">'vscode'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">activate</span>(<span class="hljs-params">context: vscode.ExtensionContext</span>) </span>{
    context.subscriptions.push(
        vscode.commands.registerCommand(<span class="hljs-string">'vscode-task-manager.openTodoManager'</span>, <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-comment">// Create and show panel</span>
            <span class="hljs-keyword">const</span> panel = vscode.window.createWebviewPanel(
                <span class="hljs-string">'todoManager'</span>,
                <span class="hljs-string">'Todo Manager'</span>,
                vscode.ViewColumn.One,
                {
                    enableScripts: <span class="hljs-literal">true</span>
                }
            );

            <span class="hljs-comment">// And set its HTML content</span>
            panel.webview.html = getWebviewContent();
        })
    );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getWebviewContent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`
        &lt;!DOCTYPE html&gt;
        &lt;html lang="en"&gt;
            &lt;head&gt;
                &lt;meta charset="UTF-8"&gt;
                &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
                &lt;title&gt;Todo Task Manager&lt;/title&gt;
            &lt;/head&gt;
            &lt;body&gt;
                &lt;h1&gt;Hello TODO&lt;/h1&gt;
            &lt;/body&gt;
        &lt;/html&gt;
    `</span>;
}
</code></pre>
<p>When you run the extension and trigger the <code>Todo Manager</code> command, it should open a new panel that display <code>Hello TODO</code>;</p>
<p>Now lets figure out how to get my react resources loaded into the HTML.</p>
<p>I need to move my reacts compiled code into the <code>dist</code> directory for my extension to use. I created a npm script inside my react project to move the folder after its finished building using <code>postbuild</code>.</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"react-scripts start"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"react-scripts build"</span>,
    <span class="hljs-attr">"postbuild"</span>: <span class="hljs-string">"rimraf ../../dist/webview &amp;&amp; mv build ../../dist/webview"</span>,
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"react-scripts test"</span>,
    <span class="hljs-attr">"eject"</span>: <span class="hljs-string">"react-scripts eject"</span>
  }
</code></pre>
<p>The location of the extensions files on the file system is conveniently attached to the <code>context</code> parameter on the <code>activate</code> function. I passed the object to my <code>getWebviewContent()</code> function where I plan to fetch all the react resources.</p>
<p>React is nice enough to offer an <code>asset-manifest.json</code> to find out the name of all the compiled assets. Using <code>path</code>, <code>context.extensionPath</code>, and <code>vscodes.Uri</code>, we can map out the physical location of the compiled react scripts, and import them into the html with VS Codes resource tags.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getWebviewContent</span>(<span class="hljs-params">context: vscode.ExtensionContext</span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">const</span> { extensionPath } = context;

    <span class="hljs-keyword">const</span> webviewPath: <span class="hljs-built_in">string</span> = path.join(extensionPath, <span class="hljs-string">'dist'</span>, <span class="hljs-string">'webview'</span>);
    <span class="hljs-keyword">const</span> assetManifest: AssetManifest = <span class="hljs-built_in">require</span>(path.join(webviewPath, <span class="hljs-string">'asset-manifest.json'</span>));

    <span class="hljs-keyword">const</span> main: <span class="hljs-built_in">string</span> = assetManifest.files[<span class="hljs-string">'main.js'</span>];
    <span class="hljs-keyword">const</span> styles: <span class="hljs-built_in">string</span> = assetManifest.files[<span class="hljs-string">'main.css'</span>];
    <span class="hljs-keyword">const</span> runTime: <span class="hljs-built_in">string</span> = assetManifest.files[<span class="hljs-string">'runtime-main.js'</span>];
    <span class="hljs-keyword">const</span> chunk: <span class="hljs-built_in">string</span> = <span class="hljs-built_in">Object</span>.keys(assetManifest.files).find(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> key.endsWith(<span class="hljs-string">'chunk.js'</span>)) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>;

    <span class="hljs-keyword">const</span> mainUri: vscode.Uri = vscode.Uri.file(path.join(webviewPath, main)).with({ scheme: <span class="hljs-string">'vscode-resource'</span> });
    <span class="hljs-keyword">const</span> stylesUri: vscode.Uri = vscode.Uri.file(path.join(webviewPath, styles)).with({ scheme: <span class="hljs-string">'vscode-resource'</span> });
    <span class="hljs-keyword">const</span> runTimeMainUri: vscode.Uri = vscode.Uri.file(path.join(webviewPath, runTime)).with({ scheme: <span class="hljs-string">'vscode-resource'</span> });
    <span class="hljs-keyword">const</span> chunkUri: vscode.Uri = vscode.Uri.file(path.join(webviewPath, chunk)).with({ scheme: <span class="hljs-string">'vscode-resource'</span> });

    <span class="hljs-keyword">return</span> <span class="hljs-string">`
        &lt;!DOCTYPE html&gt;
        &lt;html lang="en"&gt;
            &lt;head&gt;
                &lt;meta charset="UTF-8"&gt;
                &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
                &lt;title&gt;Todo Task Manager&lt;/title&gt;
                &lt;link rel="stylesheet" type="text/css" href="<span class="hljs-subst">${stylesUri.toString(<span class="hljs-literal">true</span>)}</span>"&gt;
            &lt;/head&gt;
            &lt;body&gt;
                &lt;div id="root"&gt;&lt;/div&gt;
                &lt;script crossorigin="anonymous" src="<span class="hljs-subst">${runTimeMainUri.toString(<span class="hljs-literal">true</span>)}</span>"&gt;&lt;/script&gt;
                &lt;script crossorigin="anonymous" src="<span class="hljs-subst">${chunkUri.toString(<span class="hljs-literal">true</span>)}</span>"&gt;&lt;/script&gt;
                &lt;script crossorigin="anonymous" src="<span class="hljs-subst">${mainUri.toString(<span class="hljs-literal">true</span>)}</span>"&gt;&lt;/script&gt;
            &lt;/body&gt;
        &lt;/html&gt;
    `</span>;
}
</code></pre>
<p>Now when I run the debugger for my extension and trigger the <code>Todo Manager</code> command. The React app appears as a VS Code Panel!!</p>
<h2 id="issues-and-concerns-with-current-implementation">Issues and Concerns with current implementation.</h2>
<p>I am not 100% happy with this solution. I am not a fan of a sub npm package and managing the react build separately than the extension. A great example of why I dislike it is the eslint issue I didn't expect to happen. I also dislike how I have to compile the react app separately and then compile the extension to make it work. I need to work on my npm scripts to make it more seamless.</p>
<p>One benefit of treating it like a separate app is I can run react in my browser to quickly develop the front end portion and then test it out as a webview panel later.</p>
<p>This is all just a proof of concept for now. There is a more official way to implement web views that I plan on using now that I know it works.</p>
<h2 id="next-steps">Next steps</h2>
<p>I need to figure out how to make the react app and the extension communicate with each other. I have seen some existing open source projects using RPC (Not sure what that is) but I have also seen some using a <code>postMessage() &amp;&amp; onMessage()</code> method. Over the next couple of days, I'll be investigating what I can do and document my efforts.</p>
<p>I also want a more trendy name. <code>Todo Task Manager</code> is just not sitting well with me.</p>
<p><a target="_blank" href="https://github.com/CodeByCorey/vscode-todo-task-manager">Source Code</a></p>
]]></content:encoded></item><item><title><![CDATA[Building a VSCode Extension: Part Two]]></title><description><![CDATA[Now that I have an idea of what I am going to build, It's time to set up the repository.
VSCode has a straightforward method for bootstrapping a new extension.
Making sure all the prerequisites are installed
The prerequisites for developing an extens...]]></description><link>https://blog.codebycorey.com/building-a-vscode-extension-part-two</link><guid isPermaLink="true">https://blog.codebycorey.com/building-a-vscode-extension-part-two</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Visual Studio Code]]></category><category><![CDATA[React]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Fri, 28 Aug 2020 16:02:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902774612/QJhsLp1S0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that I have an idea of what I am going to build, It's time to set up the repository.</p>
<p>VSCode has a straightforward method for bootstrapping a new extension.</p>
<h2 id="making-sure-all-the-prerequisites-are-installed">Making sure all the prerequisites are installed</h2>
<p>The prerequisites for developing an extension is having <a target="_blank" href="http://nodejs.org">Node.js</a> and <a target="_blank" href="https://git-scm.com/">Git</a> installed on your machine.</p>
<p>If you need to install Node, I recommend using <a target="_blank" href="https://github.com/nvm-sh/nvm#installing-and-updating">NVM</a> if you are on Linux or macOS and <a target="_blank" href="https://github.com/nvm-sh/nvm#installing-and-updating">NVM-windows</a> for windows.</p>
<p><em>Disclaimer: I develop on Linux, so I will be using those commands.</em></p>
<p>Install NVM using</p>
<pre><code class="lang-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
</code></pre>
<p>Restart your terminal then install node using</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Current LTS version while writing this is v12</span>
nvm install 12
<span class="hljs-comment"># I recommend setting this version as default</span>
nvm <span class="hljs-built_in">alias</span> default 12
</code></pre>
<h2 id="bootstrapping-the-extension-repository">Bootstrapping the extension repository</h2>
<p>Now that node is installed to the latest LTS, it's time to bootstrap the extension repository.</p>
<p>Navigate to wherever you want to create the repository. For me, it's in a folder called <code>workspace</code></p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/workspace
</code></pre>
<p>VSCode offers a <a target="_blank" href="https://yeoman.io/">Yeoman</a> template to generate a basic extension.</p>
<p>Install the required NPM packages globally for Yeoman and the <a target="_blank" href="https://www.npmjs.com/package/generator-code">VS Code Extension</a> template. After its installed, you can run the generator.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install the npm packages globally</span>
npm install -g yo generator-code

<span class="hljs-comment"># Running the generator</span>
yo code

<span class="hljs-comment"># ? What type of extension do you want to create? New Extension (TypeScript)</span>
<span class="hljs-comment"># ? What's the name of your extension?</span>
<span class="hljs-comment">### Press &lt;Enter&gt; to choose default for all options below ###</span>

<span class="hljs-comment"># ? What's the identifier of your extension?</span>
<span class="hljs-comment"># ? What's the description of your extension?</span>
<span class="hljs-comment"># ? Initialize a git repository? Yes</span>
<span class="hljs-comment"># ? Which package manager to use? yarn</span>
</code></pre>
<p>I decided to use yarn because I normally use NPM but I wanted to try something new.</p>
<p>Since I am hosting the code on GitHub, I create a new empty repository there. Then I linked my GitHub repository with my local one.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> vscode-todo-task-manager/
git remote add origin git@github.com:CodeByCorey/vscode-todo-task-manager.git

git commit -am <span class="hljs-string">'initialize extension'</span>

git push -u origin master
</code></pre>
<h2 id="starting-the-development-environment">Starting the development environment</h2>
<p>Now that the repository is set up, time to run it locally.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Since I am already in the project directory</span>
code .
<span class="hljs-comment"># the . means it will open the current directory in vscode</span>
</code></pre>
<p>Once VScode is open, press <code>F5</code> to compile and run the extension.</p>
<p>To verify it is running, hit (<code>ctrl+shift+p</code>) and run the hello world command.</p>
<h2 id="time-to-dig-into-the-api-docs">Time to dig into the API docs</h2>
<p>Now that I have the base project running, I need to start reading the <a target="_blank" href="https://code.visualstudio.com/api/extension-guides/overview">API Docs</a> to figure out how to start implementing the task manager.</p>
<p>I might also look at some open-source extensions to see how they implement specific features. I tend to learn a lot from reading open-source projects.</p>
]]></content:encoded></item><item><title><![CDATA[Building a VSCode Extension: Part One]]></title><description><![CDATA[I have been looking for a fun side project to work on in my free time. Building projects is the best way to actually improve your skills as a developer. I decided that I will try to document my journey publicly to help share my thoughts and decisions...]]></description><link>https://blog.codebycorey.com/building-a-vscode-extension-part-one</link><guid isPermaLink="true">https://blog.codebycorey.com/building-a-vscode-extension-part-one</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Visual Studio Code]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Thu, 27 Aug 2020 19:16:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602902727633/E9jeVNURn.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been looking for a fun side project to work on in my free time. Building projects is the best way to actually improve your skills as a developer. I decided that I will try to document my journey publicly to help share my thoughts and decisions.</p>
<h2 id="what-am-i-going-to-build">What am I going to build?</h2>
<p>I have been spending a good amount of time working on my time management. Managing my daily tasks has been something I needed to improve. I currently write down what I plan to do for the day in a notebook. Notebooks are nice but it makes it difficult to organize the tasks besides by date and order. How can I dynamically group them by project, adjust priority as my day changes, roll them over to the next day without having to rewrite them? I decided to create a task manager built into VSCode.</p>
<p>I am sure there are other extensions that already do this but that is not the point. The plan is to use this as a learning experience and build something specifically for me. I get to determine what features are important and how they tie into my workflow.</p>
<h2 id="problems-i-am-solving">Problems I am solving</h2>
<ul>
<li>I spend most of my day with VSCode open on my computer as a Programmer. It would be nice to have all my tasks for my projects in one place.</li>
<li>I want to have a board similar to Trello where I could easily click and drag to move tasks between statuses and adjust priorities.</li>
<li>Most of my daily notes are Markdown in a repository. Having the ability to link my notes through wiki links using something like Foam would be a plus.</li>
</ul>
<h2 id="thinking-through-the-tech-stack">Thinking through the Tech Stack</h2>
<p>The tech stack I plan on using:</p>
<ul>
<li>TypeScript<ul>
<li>Vscode's API already uses it.</li>
</ul>
</li>
<li>ReactJS<ul>
<li>It might be a little overkill but why not.</li>
</ul>
</li>
<li>Tailwind CSS<ul>
<li>I want to easily be able to style the webview and I love using utility classes for styles.</li>
</ul>
</li>
<li>Markdown<ul>
<li>I already take my notes in Markdown and it would make it easier integrating with foam.</li>
<li>GitHub Markdown has a task like syntax I could utilize and then you are not forced to use the extension to manage your tasks.</li>
</ul>
</li>
</ul>
<h2 id="resources-i-will-be-using">Resources I will be using</h2>
<ul>
<li><a target="_blank" href="https://code.visualstudio.com/api">VSCode Extension API</a></li>
<li><a target="_blank" href="https://tailwindcss.com/">Tailwind CSS Docs</a></li>
<li><a target="_blank" href="https://github.com/CodeByCorey/vscode-todo-task-manager">The Repository</a></li>
<li><a target="_blank" href="https://foambubble.github.io/foam/">Foam</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Being Successful While Working From Home]]></title><description><![CDATA[Working from home has some unique challenges compared to being in an office.  When I worked in the office, I quickly excelled. I became the go-to guy in a just a few months and began making important decisions for development. When I started my remot...]]></description><link>https://blog.codebycorey.com/being-successful-while-working-from-home</link><guid isPermaLink="true">https://blog.codebycorey.com/being-successful-while-working-from-home</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Career]]></category><category><![CDATA[remote]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Corey O'Donnell]]></dc:creator><pubDate>Mon, 24 Aug 2020 11:40:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602901922099/3GAe-T0-a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Working from home has some unique challenges compared to being in an office.  When I worked in the office, I quickly excelled. I became the go-to guy in a just a few months and began making important decisions for development. When I started my remote job, things did not go as well as I expected. Even though I thought I had good self-control, the freedom lead to some bad habits. My productivity suffered and my work tasks started to slip and I began to struggle. After a couple of months of barely getting my work done, I had to sit myself down and set myself straight.</p>
<h2 id="finding-my-problems">Finding my problems</h2>
<p>When you do not have someone looking over your shoulder, it's easy to just do what you want. Bad habits are easy to develop, and they quickly compound on themselves. One of the first things I did was create a list of everything I was doing throughout the day and how long I spent doing it. It did not matter if it was good or bad. I just wanted to have better insight on what I was doing. After a couple of days doing this, it quickly became clear where most of my problems were.</p>
<h2 id="created-a-game-plan">Created a game plan</h2>
<p>Now that I know my problems and bad habits, it was time to start doing something about them.</p>
<h3 id="making-all-the-bad-habits-difficult-to-do">Making all the bad habits difficult to do</h3>
<p>I had my personal computer right next to my work computer. This was mixing my personal with work and I would often drift over to my personal computer. First things I did was delete all video games off the computer and just turned it off. Just adding that small amount of resistance kept me more focused on work.</p>
<h3 id="putting-myself-in-the-work-mindset">Putting myself in the work mindset</h3>
<p>I started waking up earlier so when I walk into my home office, I am wide awake and prepared for work. I started getting dressed like I was going to the office to help put me in the work mindset. First thing walking into my office, I wrote down all the tasks that were assigned to me and all the scheduled meetings for the day. I could then prioritize everything and know what I could and could not complete for the day.</p>
<h3 id="techniques-for-staying-productive">Techniques for staying productive</h3>
<p>Being in the work mindset is a nice step for better productivity, but I still wanted to take it to the next level. I started using the Pomodoro time management technique. You set a timer for 50 minutes and work with zero distractions. When the timer went off, you give yourself a 10-minute break. This technique pushed me to be very productive. If I am really into a problem, I will usually continue past the time. Since breaks are just as important for productivity, I make up the time when I finish the task.</p>
<h2 id="everything-is-going-great">Everything is going great</h2>
<p>After thinking through all my struggles and executing my plan for productivity, I am now thriving while working from home. I am now the go-to guy on my current team and recently promoted to Senior Software Engineer. My company is currently talking about moving me to team lead role. All it took was a couple of small changes and my productivity went from nothing to being great.</p>
]]></content:encoded></item></channel></rss>